线程概念:
进程:有独立的 进程地址空间。有独立的 pcb。 是分配资源的最小单位。
线程:有独立的 pcb。没有独立的进程地址空间。 是执行的最小单位。
查看线程号:ps -Lf 所查看的pid
轻量级进程(light-weight process),也有 PCB,创建线程使用的底层函数和进程一样,都是clone 。从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB 中指向内存资源的三级页表是相同的。进程可以蜕变成线程,线程可看做寄存器和栈的集合。
线程独享栈空间,共享全局变量。
创建线程pthread_create
int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*start_rountn)(void *), void *arg);
参 1:传出参数,表新创建的子线程 id。
参 2:线程属性。传 NULL 表使用默认属性。
参 3:子线程回调函数。创建成功,ptherad_create 函数返回时,该函数会被自动调用。
*与创建进程不一样,线程只会执行参3的函数,不会继续执行主线程之后的代码。
参 4:参 3 的参数。没有的话,传 NULL。
返回值:成功:0。
失败:errno。
获取线程 id pthread_self
pthread_t pthread_self(void); 线程 id 是在进程地址空间内部,用来标识线程身份的 id 号。
返回值:本线程 id。
线程退出 pthread_exit
void pthread_exit(void *retval); 退出当前线程。
retval:退出值。 无退出值时,NULL。即调用函数的返回值。
*线程退出的三种方式:在线程函数中return;用pthread_exit终止自己;用pthread_cancel终止其他。
*由于主线程可能先于子线程结束,所以子线程的输出可能不会打印出来,当时是用主线程 sleep 等待子线程结束来解决的。现在就可以使用 pthread_exit 来解决了。方法就是将 return 0 替换为 pthread_exit,只退出当先线程,不会对其他线程造成影响。
线程回收 pthread_join
阻塞当前线程,等待对应线程执行结束后回收线程。
int pthread_join(pthread_t thread, void **retval);
thread: 待回收的线程 id。
retval:传出参数。 回收的那个线程的退出值。线程异常结束,值为 -1。
返回值:成功:0。
失败:errno。
线程终止 pthread_cancel
杀死一个线程。 需要到达取消点(保存点)。
int pthread_cancel(pthread_t thread);
thread: 待杀死的线程 id。
返回值: 成功:0。
失败:errno。
如果,子线程没有到达取消点, 那么 pthread_cancel 无效。
我们可以在程序中,手动添加一个取消点。使用 pthread_testcancel();
成功被 pthread_cancel() 杀死的线程,返回 -1.使用 pthead_join 回收。
线程分离 pthread_detach
线程分离的本质是让主线程不join新线程,不关心返回值,从而让新线程退出的时候自动回收。
int pthread_detach(pthread_t thread);
thread: 待分离的线程 id。
返回值: 成功:0。
失败:errno。
线程分离后,系统会自动回收资源,用 pthread_join 去回收已经被系统回收的线程,那个线程号就是无效参数。
可以运用线程属性设置分离线程。
pthread_attr_t attr //创建一个线程属性结构体变量
pthread_attr_init(&attr); //初始化线程属性
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //设置线程属性为分离态
pthread_create(&tid, &attr, tfn, NULL); //借助修改后的 设置线程属性 创建为分离态的新线程
pthread_attr_destroy(&attr); //销毁线程属性
注意事项
1.主线程退出其他线程不退出,主线程应调用pthread_exitw
2.避免僵尸线程。pthread_join;pthread_detach;pthread_create指定分离属性,被join线程可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值。
3. malloc和 mmap申请的内存可以被其他线程释放。
4.应避免在多线程模型中调用fork,除非马上 exec,子进程中只有调用fork的线程存在,其他线程在子进程中均pthread_exit。
5.信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制。