线程创建的函数及应用小结
进程是计算机分配资源的基本单位,线程是cpu调度的基本单位
线程基本概念:
LWP:light weight process 轻量级的进程。创建线程的底层函数和进程一样,都是clone,因此线程的本质仍是进程(在linux环境下)
与进程相比,线程有独立的TCB结构体(类似于进程的PCB),但没有独立的地址空间(共享),类似于合租与独居。
查看线程号(LWP,不是TID)可以用下述命令:
ps -Lf xxx(PID)
线程创建相关函数
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
//=================================属性设置按需要采用,若普通情况可以不用设置===============//
pthread_attr_t attr; // 定义线程的属性变量
int pthread_attr_init(pthread_attr_t *attr); // 初始化属性变量
int pthread_attr_setXXX(pthread_attr_t *attr, ...); // 设置属性变量的值
//此处仅列举常用的//
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); // 设置/获得线程的分离属性
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched); // 设置/获得是否继承创建者的调度策略
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); // 设置/获得调度策略
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); // 设置/获得线程的静态优先级
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
//=================================上述属性设置按需要采用,若普通情况可以不用设置===============//
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
// 创建线程,此处void *(*start_routine)(void *)函数指针指向的函数,void *arg作为其参数,并且需要采用值传递,如果数据类型不同需要强转
int pthread_detach(pthread_t thread); // 如果没有设置线程的分离属性,可以用此函数强制设为分离属性,防止线程结束后变为僵尸进程,无法释放资源,而且不能再用 pthread_join等待回收该进程的资源
pthread_t pthread_self(void); // 创建成功后,可以在线程中使用,获取当前线程的tid
void pthread_exit(void *retval); // 退出线程,并得到返回值retval
int pthread_cancel(pthread_t thread); // 给指定线程发送一个取消的请求
int pthread_setcancelstate(int state, int *oldstate);
// 设置线程的取消状态:PTHREAD_CANCEL_ENABLE 可取消; PTHREAD_CANCEL_DISABLE 不可取消
int pthread_setcanceltype(int type, int *oldtype);
// 设置线程的取消类型:PTHREAD_CANCEL_DEFERRED 延时响应; PTHREAD_CANCEL_ASYNCHRONOUS 立即响应。
int pthread_join(pthread_t thread, void **retval); // 接合指定已结束或者待结束的线程,并得到返回状态值;如果指定的线程还在运行,将会阻塞等待。
应用实例
char *retval = "byebye!\n"; // 假设为共享资源
pthread_mutex_t m; // 定义互斥锁
void handler(void *arg)
{
pthread_mutex_unlock(&m);
}
void *child_thread(void *arg)
{
while (1)
{
pthread_cleanup_push(handler, NULL); // 上锁前,需要将handler函数压入线程取消处理的栈中,以防止该子进程在运行的中途被取消,造成死锁
pthread_mutex_lock(&m);
printf("Child thread obtain the mutex:%s\n", retval);
pthread_mutex_unlock(&m);
pthread_cleanup_pop(0); // 解锁后,将handler从栈中弹出,不执行
sleep(2);
}
}
int main()
{
pthread_mutex_init(&m, NULL);
pthread_t tidofparent = pthread_self(); // 接收主线程的tid值。
printf("parent's tid:%ld\n", tidofparent);
printf("parent's tid:%ld\n", sizeof(int));
pthread_t tid;
pthread_create(&tid, NULL, child_thread, NULL); // 新建子线程
// pthread_detach(tid); 让线程“自立门户”,结束后资源自动回收,此处与pthread_cancel和pthread_join冲突,
sleep(5);
pthread_cancel(tid); // 5秒后,向子线程发送取消的请求
// 待子线程被取消后,锁被handler自动释放,可以继续加互斥锁,对公共资源进行操作
pthread_mutex_lock(&m);
retval = "I'm main pthread!\n";
printf("Now I botain the mutex:%s\n", retval);
pthread_mutex_unlock(&m);
/*
void *p;
pthread_join(tid, &p); // 如果没有采用pthread_cancel(tid)取消子进程,这部分将会阻塞等待子线程结束,接受子线程的返回值并打印
printf("子线程的返退出回值:%s\n", (char *)p);
*/
return 0;
}
注意事项
1.主线程退出也可以用pthread_exit(),其他子线程照样运行不受影响。但如果主线程运行了return/exit等语句,或者子线程用exit,会导致整个进程退出。
2.避免线程编程僵尸进程的三种方法:
- int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) 提前设置分离属性
- int pthread_detach(pthread_t thread);子线程创建后强制设置分离属性
- 在线程结束后利用pthread_join()进行接合。
3.malloc 和mmap 申请的内存可以被其他子线程释放,因为申请出来的都是共享堆地址。
4.信号语义复杂,尽量避免和线程机制混用。