学习线程是一件很好玩的是,它不像进程那样复杂而且很好用。下面是我写的一段代码,附有一定的解释和说明,以及一些思考,看懂了我这段代码和注释,那么线程编程就入门了,合适新手研究。不对的地方请多多指教啊。
代码由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; }