电子政务网站建设ppt,长春seo培训,电子商务网站建设招标书,做视频网站注意#xff1a;使用线程库函数用gcc编译时#xff0c;要加参数#xff1a;-lpthread#xff08;libpthread.so#xff09;#xff0c;因为线程库函数属于第三方c库函数#xff0c;不是标准库函数#xff08;/lib、/usr/lib或者/usr/local/lib#xff09;。
#xf…注意使用线程库函数用gcc编译时要加参数-lpthreadlibpthread.so因为线程库函数属于第三方c库函数不是标准库函数/lib、/usr/lib或者/usr/local/lib。
1pthread_self函数
获取线程ID。其作用对应进程中 getpid() 函数。
pthread_t pthread_self(void); 返回值成功调用该函数的线程ID失败永远不会失败。
pthread_ttypedef unsigned long int pthread_t; 输出格式格式为%lu
线程ID是进程内部识别标志。两个进程间线程ID允许相同
注意不应使用全局变量 pthread_t tid因为全局变量被一个进程中的多个线程共享在子线程中通过pthread_create传出参数来获取线程ID或者使用pthread_self。
2pthread_create函数
创建一个新线程。其作用对应进程中fork() 函数。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
返回值成功0失败错误编号。Linux环境下所有线程特点失败均直接返回错误编号即直接返回errno number。
参数1传出参数新建线程的线程ID
参数2传入参数通常传NULL表示使用线程默认属性。若想使用具体属性也可以修改该参数。
参数3函数指针指向线程主控函数线程体该函数运行结束则线程结束。
参数4线程主函数执行期间所使用的参数若不同参数传NULL。
在一个线程中调用pthread_create()创建新的线程后当前线程从pthread_create()返回继续往下执行而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定线程创建成功后就立即去执行start_routine函数。start_routine函数接收一个参数是通过pthread_create的arg参数传递给它的该参数的类型为void *这个指针按什么类型解释由调用者自己定义。start_routine的返回值类型也是void *这个指针的含义同样由调用者自己定义。start_routine返回时这个线程就退出了其它线程可以调用pthread_join得到start_routine的返回值类似于父进程调用wait( )得到子进程的退出状态稍后详细介绍pthread_join。
pthread_create成功返回后新创建的线程的id被填写到thread参数所指向的内存单元。我们知道进程id的类型是pid_t每个进程的id在整个系统中是唯一的调用getpid( )可以获得当前进程的id是一个正整数值。线程id的类型是pthread_t它只在当前进程中保证是唯一的在不同的系统中pthread_t这个类型有不同的实现它可能是一个整数值也可能是一个结构体也可能是一个地址所以不能简单地当成整数%lu用printf打印调用pthread_self( )可以获得当前线程的id。
attr参数表示线程属性本节不深入讨论线程属性所有代码例子都传NULL给attr参数表示线程属性取缺省值感兴趣的读者可以参考APUE。
//pthread_self和pthread_create函数示例
#include stdio.h
#include pthread.h
#include stdlib.h
#include string.h
#include unistd.hvoid *tfn(void *arg)
{printf(In thread, the process id is %d and thread id is %lu.\n,getpid( ),pthread_self( ));return NULL;
}int main(void)
{pthread_t tid;int ret;printf(In main1, the process id is %d and thread id is %lu.\n,getpid( ),pthread_self( ));ret pthread_create(tid, NULL, tfn, NULL);if( ret ! 0 ) //出错判断{fprintf(stderr,pthread_create error: %s\n,strerror(ret));exit(1);}printf(the created threads id is %lu\n,tid);sleep(1); //注意一定要睡1sprintf(In main2, the process id is %u and thread id is %lu.\n,getpid( ),pthread_self( ));return 0;
}
[rootlocalhost 01_pthread_test]# ./pthrd_crt
In main1, the process id is 9814 and thread id is 4151932608.
the created threads id is 4149869376
In thread, the process id is 9814 and thread id is 4149869376.
In main2, the process id is 9814 and thread id is 4151932608.
分析
在main函数中调用sleep函数的原因。在Linux环境中进程的本质就是含有一个线程的进程其在创建线程之前就包含一个线程因此此时有一个进程ID和一个线程ID当然也有一个线程号两个ID不想等将此时的线程称为主控线程后面创建的线程称为子线程。主线程创建完子线程后如果主线程提前就结束了执行了return或者exit等语句则此时进程的地址空间会被回收因此子线程也就被终止了因为线程共享地址空间。因此主线程main函数必须要睡1s等待子线程完成工作后再结束。注意线程库函数调用失败返回值的判断。出错不会再置ennro的值为相应的值而是直接输出错误编号因此不能再用perror函数来输出详细错误信息该函数是根据errno的值输出相应的错误信息。可以采用strerror函数其作用返回一个字符串这个字符串描述了错误编号所对应的错误信息。char * strerror(int errnum); 如上所示fprintf(stderr,pthread_create error: %s\n,strerror(ret)); 采用fprintf函数将错误信息输出。相对于printffprintf函数可以将内容输出到指定文件中第一个参数为文件的结构体指针。printf与fprintf都是标准输出stdin、stdout和stderr为标准输入、标准输出和标准错误输出对应的文件结构体。输出结果可以看出在创建子线程前进程本身包含一个线程同一个进程中的所有线程的进程ID都一样线程ID明显比线程号和进程ID大得多。
总结由于pthread_create的错误码不保存在errno中因此不能直接用perror( )打印错误信息可以先用strerror( )把错误码转换成错误信息再打印。为了防止新创建的线程还没有得到执行就终止我们在main函数return之前延时1秒这只是一种权宜之计即使主线程等待1秒内核也不一定会调度新创建的线程执行后面会有更好的办法。
//循环创建n个子线程的架构
#include stdio.h
#include pthread.h
#include stdlib.h
#include string.h
#include unistd.hvoid *tfn(void *arg)
{int s (int)arg;sleep(s);printf(The %dth thread: the process id is %d and thread id is %lu.\n,s1,getpid( ),pthread_self( ));return NULL;
}int main(void)
{pthread_t tid;int ret, i;for( i0;i5;i ){ret pthread_create(tid, NULL, tfn,(void *)i);if( ret ! 0 ){fprintf(stderr,pthread_create error: %s\n,strerror(ret));exit(1);}}sleep(i);printf(In main: the process id is %u and thread id is %lu.\n,getpid( ),pthread_self( ));return 0;
}
[rootlocalhost 01_pthread_test]# ./pthrd_crt
The 1th thread: the process id is 4026 and thread id is 4149246784.
The 2th thread: the process id is 4026 and thread id is 4140854080.
The 3th thread: the process id is 4026 and thread id is 4132461376.
The 4th thread: the process id is 4026 and thread id is 4124068672.
The 5th thread: the process id is 4026 and thread id is 4115675968.
In main: the process id is 4026 and thread id is 4151466240.
分析
如上循环创建5个线程该进程中有6个线程进程ID均相同线程ID不一样每一个进程中线程的数目是有限的不能超过这个值特别强调主控线程调用的pthread_create函数中的第4个参数(void *)i不能改为(void *)i相应在子线程执行的函数tfn中为s *( (int *)arg ); 详细分析如下首先main函数是主控线程执行的函数因此位于主控线程的用户栈帧空间中该空间保存了main函数中的局部变量和形参值tid、red、i当main函数又去调用pthread_create函数时main函数的栈帧头尾作为临时值保存在栈帧空间中。pthread_create函数的形参值保存在它的栈帧空间中。对于tfn函数子线程执行的函数它的局部变量和形参值s和arg又保存在各个线程的用户栈空间中相互独立。外界参数的值传递给函数的形参时都是按值传递指针则传地址值非指针变量则传变量值如果传参数(void *)i则将数值i转变为指针值 指针值与数值i在大小上是相等的只是在32位系统中指针值和整型i都占据4个字节在64位系统中指针值占据8个字节而整型i依然占据4个字节此时高位补0即可。然后将该指针值赋值给指针变量arg即arg的值大小上是等于i的但arg位于子线程的用户栈空间中然后(int)arg即把指针值转变为整型ss当然等于i因此该方式是正确的。在64位系统中指针值转变为整型i8字节变为4字节截取高位而高位都是0因此无影响。但如果是(void *)i *( (int *)arg)这种情况下传递的是i的地址给arg因此在子线程中是通过i的地址来访问数值i的而数值i在主控线程中是会随时发生变化的for循环中的i导致因此该方式不成立。pthread_create函数是一个回调函数若创建子线程成功则会去调用相应的函数子线程执行完自己的函数后返回的值通过pthread_join函数回收return的作用是返回到函数的调用点如果是main函数中的return则代表该进程结束并释放进程地址空间所有线程都终止。对于其它函数的return则直接返回到函数的调用点。exit和_exit函数会直接终止整个进程导致所有线程结束。pthread_exit函数则会导致调用该函数的线程结束。所以多线程环境中应尽量少用或者不使用exit函数取而代之使用pthread_exit函数将单个线程退出。任何线程里exit导致进程退出其他线程也结束主控线程退出时不能return或exit。