学习线程是一件很好玩的是,它不像进程那样复杂而且很好用。下面是我写的一段代码,附有一定的解释和说明,以及一些思考,看懂了我这段代码和注释,那么线程编程就入门了,合适新手研究。不对的地方请多多指教啊。
代码由A、B、C、D、E、EE六个线程,其功能有,主线程传参(结构体、整形、字符串)、子线程返回参数的两种方法、创建双层线程(父子孙,不建议这样做)以及他们之间的参数传递与计算等,好好研究吧。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
struct menber
{
int a;
char *s;
};
struct fo
{
int a,b,c,d;
}foo={1,2,3,4};
void printfoo(const char *s,const struct fo *fp)
{
printf("%s",s);
printf("foo.a=%d\n",fp->a);
printf("foo.b=%d\n",fp->b);
printf("foo.c=%d\n",fp->c);
printf("foo.d=%d\n",fp->d);
}
void *thr_D(void *arg)//子线程传给父线程一个结构体
{
printfoo("我是线程D:\n",&foo);
pthread_exit((void *)&foo);//这个地方的&引用一定不要丢,丢了,编译通过但运行出现段错误
}
void *thr_C(void *arg)//传递字符串
{
char *name=(int *)arg; //-Wall编译时出现警告:从不兼容的指针尅想初始化
// sleep(1);
printf("我是线程C,ID = %u,得到的字符串name=%s\n",(unsigned int)pthread_self(),name);
return (void *)0;
}
void *thr_B(void *arg)//传递整形
{
int *m =(int *)arg;
printf("我是线程B,ID = %u,得到的整形m=%u\n",(unsigned int)pthread_self(),*m);
return (void *)0;
}
void *thr_A(void *arg)//传递结构体
{
struct menber *temp;
temp=(struct menber *)arg;
printf("我是线程A,ID = %u,我从进程得到strut:",(unsigned int)pthread_self());
printf("menber->a = %d ; ",temp->a);
printf("menber->s = %s \n",temp->s);
return (void *)0;
}
void *thr_EE(void *arg)//父子间互相通信
{
int c;
int *b=(void *)arg;
printf("我是sun线程,");
printf("请输入一个数:");
scanf("%d",&c);//在%d后面不能加\n,不然执行到这句后,就不在执行了
c=c*(*b);
printf(" 经过计算*参数值得到c = %d\n",c);
return (void *)c;
//这是线程与进程之间互相传至的程序,在线程中可以使用return 和
//pthread_exit,返回值到进程,效果是一样
}
void *thr_E(void *arg)//父子间互相通信
{
pthread_t EE;
int *aa=(int *)arg;
int x =1;
int *atr = &x;
int *y;
printf("我是zi线程,我得到了父线程的参数:%d\n",*aa);
x=x+ (*aa);
printf(" 经过我的计算加1得出:%d\n",x);
pthread_create(&EE, NULL, thr_EE,(void *)atr);
pthread_join(EE,(void *)&y);
printf("zi经过孙线程计算得到:%d\n",y);
pthread_exit((void *)y);
}
int main(int argc,char *argv[])
{
pthread_t A,B,C,D,E;
int a,*count;
int *atr=&a;
int error;
struct menber *b;
struct fo *fp;
int m =1;
int *attr=&m;
char *name ="My name is BHW!";
b=(struct menber *)malloc( sizeof(struct menber) );
b->a = 4;
b->s = "我是hubbybob";
//3个思考题:
//1:3个join分别在creat函数下,会是什么结果?
//2:把3个join放到所有creat后面,回事什么结果?
//3:把3个join去掉,回事什么结果?
error = pthread_create(&C, NULL, thr_C, (void *)name);
error = pthread_create(&B, NULL, thr_B, (void *)attr);//如果传入的参数为多个,必须以结构体的形式传入
error = pthread_create(&A, NULL, thr_A, (void *)b);
//pthread_join(C, NULL);
// pthread_join(B, NULL);
// pthread_join(A, NULL);
//答案:
//1:打印的结果是A-B-C的顺序,但是其线程的ID是一样的
//2:不管ABC的join的顺序如何,打印的结果只与creat的顺序,先执行最后一个,而且线程ID不一样,增大,为什么?
//3:去掉join后打印的顺序为CBA,打印顺序与creat的顺序有关,先执行最后一个,且线程ID不一样,增大,为什么?
//线程创建时不保证哪个线程先运行,其所有的现象调试都可用sleep,来调试,效果是很明显的,大家可以试一试
//并且可以通过sleep来调节他们的占空比
sleep(1);
printf("下面是由线程传给进程的参数:\n");
error = pthread_create(&D, NULL, thr_D, NULL);
pthread_join(D,(void *)&fp );
//这里的&引用符号不能少,少了后,编译通过,但运行发生段错误
//放生段错误,修改正确之后,再次编译会打印这样一条消息
//"You have new mail in /var/spool/mail/root"这是怎么回事呢?
sleep(1);
printfoo("我是D传回的:\n",fp);
sleep(1);
printf("下面在线程中创建一个线程,但是不推荐使用:\n");
//这是个很简单的多线程测试程序,在主线程中创建线程E ,在E 中创建EE ,
//其功能是实现 count=(x+1)*y,x有主线程输入,y有孙线程输入
//但是这是比较麻烦的,因为猪线程传值给E,E再传给EE,返回时EE返回给E,E再返回给主线程,
//有没有办法直接由EE返回值给主线程???
printf("我是父线程,请输入主参数a:");
scanf("%d",&a);
error = pthread_create(&E, NULL, thr_E,(void *)atr);
pthread_join(E,(void *)&count);
printf(" 经过zi与孙的计算得到count=%d\n",count);//-Wall选项编译时出现警告:%d为int,而参数为int *
sleep(2);
return 0;
}
这段代码的结果:
另一段代码的研究:其结果就不附图了,感兴趣的话,可以用sleep函数来控制,亲自体验一下。
//这是一个很有意思的程序,其输出的顺序是可以改变的:
//1.当加上pthread_join()这个函数时,其顺序是先执行线程的内容,进程阻塞赛直至线程结束,但也与此函数的位置有关
//2.当不加pthread_join()这个函数时,其顺序可由sleep()函数控制
//3.当进程中的sleep去掉后则只能看到进程的内容被打印,是因为没有阻塞时,直接执行了return,结束了进程
//4.sleep可以控制线程与进程的占空比
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
void *thread(void *str)
{
int i;
for (i = 0; i < 6; ++i)
{
sleep(3);
printf( "This in the thread : %d\n" , i );
}
return NULL;
}
int main()
{
pthread_t pth;
int i;
int ret = pthread_create(&pth, NULL, thread, (void *)(i));
// pthread_join(pth, NULL);
printf("123\n");
for (i = 0; i < 4; ++i)
{
sleep(1);
printf( "This in the main : %d\n" , i );
}
pthread_join(pth, NULL);
return 0;
}