目录
1.线程
与进程类似,线程(thread)是允许程序并发执行多任务的一种机制。一个进程包含多个线程,每个线程会独立执行相同的程序代码,且共享同一份全局内存区域。
同一进程中的多个线程可以并发执行。在多处理器环境下,多个线程可以同时并行。如果一个线程因等待I/O操作而堵塞,那么其他线程依然可以继续运行。
线程相对于进程的优势:
1.进程间的信息难以共享,需要进程间通信。线程间能够方便、快速的共享信息,不管要避免多个线程修改同一数据的情况。
2.调用fork函数创建进程的代价比较高,需要复制父进程的内容。线程比进程的创建要快的多。
2.创建线程
启动程序时,产生的进程只有单条线程,称之为初始或者主线程。函数pthread_create()负责创建一条新的线程。新线程通过调用带有参数的arg函数start_routine而开始执行。调用pthread_create()的线程会继续执行该调用之后的语句。
进程内部的每个线程都有一个唯一的标识,称为线程ID。线程获取自己的线程ID使用pthread_self()函数。
使用ps-eLf查看线程
使用gcc编译,要加参数lpthread
pthread_create函数
函数描述:
创建一个线程
头文件:
#include<pthread.h>
函数原型:
int pthread_create(pthread_t *thread,const pthread_attr *attr,void *(*strt_routine)(void*),void *arg );
函数参数:
thread:传出参数,保存系统为我们分配好的线程ID
attr:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改参数
start_routine:函数指针,指向线程主函数(线程体),该函数运行结束,则该线程结束
arg:线程主函数参数
函数返回值:
成功返回0
失败返回错误号
pthread_self函数
函数描述:
获取线程ID。类似与进程中的getpid函数。线程ID是进程内部的识别标识(两个进程间,运行线程ID相同)。
函数原型:
pthread_t pthread_self(void);
函数返回值:
成功返回线程ID,pthread_t为无符号整型(%lu)
失败返回无
例如下面一段代码,
int a=0;
void* thread1(void* arg){
printf("thread tid=%lu\n",pthread_self());
a=1;
return NULL;
}
int main(int argc,char* argv[]){
pthread_t tid;
pthread_create(&tid,NULL,thread1,NULL);
printf("main tid=%lu\n",pthread_self());
sleep(1);
printf("a=%d\n",a);
return 0;
}
通过创建一个线程,让这个线程去修改a的值。
注意编译的语句为gcc -o fun fun.c -lpthread
3.终止线程
终止线程的方式
1.线程start_routine函数执行return
2.线程调用pthread_exit()函数
3.任意线程调用exit(),或者主线程执行了return(在main函数中执行return)
4.调用pthead_cancel()取消指定线程
pthread_exit函数将终止调用线程,且其返回值可由另一线程调用pthread_join()来获取。调用pthread_exit()相当于在线程的start_routine函数中执行return,不同之处在于,可在线程start_routine函数所调用的任意函数中调用pthread_exit()都能够直接终止线程。
pthred_exit函数
函数描述:
终止线程
函数原型
void pthread_exit(void* retval);
函数参数:
retval:表示线程退出状态,通常传NULL;
例如在上述代码中修改一下thread1函数
int a=0;
void* thread1(void* arg){
printf("thread tid=%lu\n",pthread_self());
pthread_exit(NULL);
a=1;
return NULL;
}
int main(int argc,char* argv[]){
pthread_t tid;
pthread_create(&tid,NULL,thread1,NULL);
printf("main tid=%lu\n",pthread_self());
sleep(1);
printf("a=%d\n",a);
return 0;
}
在执行thread1这个线程时,就会打印后直接结束这个线程,而不会去执行后面的语句,所以a的值为0.
pthread_cancel函数
函数描述:
向指定线程发送一个取消请求。发出取消请求后,函数pthread_cancel()当即返回,不会等待目标线程的退出。被请求取消的线程不会立即取消,需要等待到达某一个取消点。被取消的线程返回值是PTHREAD_CANCELED(-1)
函数原型
int pthread_cancel(pthread_t thread);
函数参数:
thread:要取消的线程ID
函数返回值:
成功返回0
失败返回错误号
int a =0;
void* thread1(void*arg){
while(1){
if (a == 10){
a =0;
}
a++;
pthread_testcancel();
}
}
int main(int argc,char* argv[]){
pthread_t tid;
pthread_create(&tid,NULL,threadl,NULL);
int ret =pthread_cancel(tid);
if (ret != 0)
printf("pthread cacel error,ret = %d\n",ret);
else
printf("pthread cacel success,ret =%d\n",ret);
while(1){
printf("a=%d\n",a);
sleep(1);
}
}
在主函数中,创建了一个线程,然后thread函数力扣执行,然后调用函数pthread_cancel函数取消,在thread1函数中,pthread_testcancel()函数是一个检测点,当发现有取消这个线程的请求时,就会终止这个线程。
4.线程的连接与分离
当一个线程结束时,它所占用的系统资源(如栈空间等)不会立即被释放,而是处于一种 “僵尸” 状态,等待其他线程调用pthread_join
来回收这些资源。如果不调用pthread_join
,可能会导致资源泄漏,尤其是在频繁创建和销毁线程的情况下。
pthread join 函数
函数描述:
等待指定线程终止并回收,这种操作叫做连接(joining)。未连接的线程会产生僵尸线程,类似僵尸进程的概念。
函数原型:
int pthread join(pthread t thread, void **retval);
函数参数:
thread:要等待的线程ID
retval:存储线程返回值
函数返回值:
成功返回 0
失败返回错误号
int a=0;
void* thread1(void* arg)
{
sleep(1);
a++;
return NULL;
}
int main(int argc,char* argv[]){
pthread_t tid;
pthread_create(&tid,NULL,threadl,NULL);
pthread_join(tid,NULL);
printf("a=%d\n",a);
return 0;
}
在这个函数中,将thread1函数连接,主线程只有等thread1函数执行结束后,才会结束自己。
pthread detach 函数
函数描述:
默认线程是可连接的(ioinable),当线程退出时,其他线程可以通过调用pthread join()获取其返回状态。有时,程序员并不关心线程的返回状态,只是希望系统在线程终止时能够自动清理并移除。这时可以调用 pthread detach()函数,将线程标记为分离(detached)状态。
函数原型:
int pthread detach(pthread t thread);
函数参数:
thread:要分离的线程ID
retval:存储线程返回值
函数返回值:
成功返回 0
失败返回错误号