线程终止
exit是危险的
如果进程中的任意一个线程调用了exit,那么整个进程终止。
不终止进程的退出方式
普通单个线程的退出方法,以下方法退出不会导致进程终止:
(1)从启动例程中返回,返回值是线程的退出码(return)。
(2)线程可以被同一进程的其他线程取消。
(3)线程调用pthread_exit(void *rval)函数,rval是退出码。
例子
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>void *thread_func(void *arg){char *num=(char *)arg;if(strcmp("1",num)==0){printf("new thread return\n");return (void *)1;}else if(strcmp("2",num)==0){printf("new thread pthread_exit\n");pthread_exit((void*)2);}else if(strcmp("3",num)==0){printf("new thread exit\n");exit(3);}return (void *)0;
}int main(int argc,char *argv[]){pthread_t ntid;int err;err=pthread_create(&ntid,NULL,thread_func,argv[1]);if(err!=0){printf("new thread create failed!\n");return 0;}pthread_join(ntid,NULL ); printf("main thread\n");return 0;}
运行结果
machine:~/Desktop/C/thread$ ./pthread_exit 1
new thread return
main threadmachine:~/Desktop/C/thread$ ./pthread_exit 2
new thread pthread_exit
main threadmachine:~/Desktop/C/thread$ ./pthread_exit 3
new thread exit
return和pthread_exit()的区别
应用场景 | 推荐方法 | 选择依据 |
---|---|---|
线程入口函数正常返回 | return | 语法简洁,符合常规函数返回逻辑 |
子函数中终止线程 | pthread_exit() | return 仅返回上层函数,无法直接终止线程 |
主线程退出但不影响子线程 | pthread_exit() | 使用return 会导致进程退出,从而强制终止所有子线程 |
线程连接
pthread_join()
1.函数原型:
int pthread_join(pthread_t tid, void **rval);
2.功能描述:
pthread_join函数用于等待指定线程结束,并以阻塞的方式执行。调用该函数的线程会一直阻塞,直到指定的线程tid调用pthread_exit、从启动例程返回或者被取消。
3.参数说明:
tid:指定线程的ID,即要等待的线程的标识符。
rval:指向指针的指针,用于获取被等待线程的返回值。如果线程被取消,rval被置为PTHREAD_CANCELED。
4.返回值:
成功时返回0,失败时返回错误码。
5.线程状态:
调用pthread_join会使指定的线程处于可连接(joinable)状态等待回收。如果指定线程已经处于分离(detached)状态,那么调用pthread_join会失败。
注意:每个线程只能被 join 一次。
6.显式回收:
对于处于 joinable(可结合)状态的线程(默认创建的线程),必须主动调用 pthread_join() 函数来阻塞等待其终止并释放其占用的系统资源(如栈空间、线程描述符等)。
显式回收 = 对 joinable 线程调用 pthread_join()。 忽略此操作将导致资源持续占用,最终可能耗尽系统资源。
pthread_detach()
pthread_detach(pthread_t thread);
1.用于分离一个线程,使其处于分离状态。线程可以自己分离自己。
2.detach不会阻塞调用者,会立即返回。
3.不关心执行结果的后台任务,后台任务线程不需要与其他线程同步。
4.成功返回0,失败返回错误码。
5.线程终止时自动释放资源,无需其他线程回收。
注意:
调用限制:detach后的线程不能再join。
实践
1.阻塞方式,需要获取线程返回值或确认完成时用pthread_join(),主线程等待子线程完成工作。
2.不关心执行结果的后台任务用pthread_detach()。
线程取消
取消函数
int pthread_cancel (pthread_t tid)
功能:取消tid指定的线程。
返回值:成功返回0。
重要说明:
1.取消只是发送一个请求,并不意味着等待线程终止。
2.发送成功也不意味着tid一定会终止。
一个线程到底会不会终止,具体得看它的取消状态。
取消状态
取消状态:就是线程对取消信号的处理方式,忽略或者响应。
1. 默认行为:
线程在创建时,默认会响应取消信号。
2. 设置取消状态:
pthread_setcancelstate(int state,int *oldstate)
使用pthread_setcancelstate(int state,int *oldstate)函数可以设置本线程对取消信号的反应。该函数有两个参数:
state:指定新的取消状态,可以是PTHREAD_CANCEL_ENABLE(缺省,收到信号后线程设为CANCELED状态)或PTHREAD_CANCEL_DISABLE(忽略取消信号继续运行)。
oldstate:如果不为NULL,则存入原来的取消状态以便恢复。
3. 取消状态的重要性:
在多线程编程中,正确设置线程的取消状态可以防止线程在不应该被终止的时候被取消,从而避免数据不一致或资源泄露等问题。
通过合理设置取消点(使用pthread_testcancel()函数),可以在安全的位置终止线程,进一步确保程序的稳定性和可靠性。
一个线程如果响应取消信号要进行终止了,那么什么时候会发生终止呢?是延迟终止还是立马终止?具体得要看取消类型。
取消类型
线程取消类型是指线程对取消信号的响应方式,主要有两种:
1.延时取消(PTHREAD_CANCEL_DEFERRED)(默认):
线程在收到取消信号后,会继续运行至下一个取消点再退出。这是线程创建时的默认取消类型。
2.立即取消(PTHREAD_CANCEL_ASYNCHRONOUS):
线程在收到取消信号后,会立即执行取消动作并退出。
可以通过pthread_setcanceltype函数来设置线程的取消类型,其函数原型为:
int pthread_setcanceltype(int type, int *oldtype);
1.type参数指定新的取消类型,取值为PTHREAD_CANCEL_DEFERRED或PTHREAD_CANCEL_ASYNCHRONOUS。
2.oldtype参数如果不为NULL,则函数会将原来的取消类型值存入该指针所指向的位置。
3.仅当取消状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出(延迟取消)和立即执行取消动作(退出)(立即取消)。
注意:
设置取消类型时,需确保线程的取消状态为Enable(可通过pthread_setcancelstate函数设置),否则取消类型设置将无效。
取消点
取消一个线程时,通常需要该线程的配合。线程在运行过程中会主动检查是否有取消请求,这些检查点被称为取消点。
包含取消点的常见函数和系统调用有:
pthread_join():等待线程结束。
pthread_testcancel():测试线程是否被取消。
pthread_cond_wait() 和 pthread_cond_timedwait():条件变量等待。
sem_wait():信号量等待。
sigwait():等待信号。
write 和 read:文件读写操作。
printf();
大多数会阻塞的系统调用。
例子
#include <stdio.h>
#include <pthread.h>void *thread_func(void *arg){int stateval=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);if(stateval!=0){printf("set cancel state failed\n");}printf("new thread:set cancel disable\n");sleep(4);pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL); //立马取消,无需等待特定的取消点。不设置默认就是延迟取消printf("set cancel enable\n"); //消息点1printf("I am new thread\n"); //消息点2return (void*)10;
}int main(int argc,char *argv[]){pthread_t ntid;void *rval;int err=pthread_create(&ntid,NULL,thread_func,NULL);if(err!=0){printf("thread create failed\n");return -1;}sleep(2);printf("main thread\n");int cval=pthread_cancel(ntid);if(cval!=0){printf("thread cancel failed\n");}pthread_join(ntid,&rval);printf("main thread\n");printf("cval is %d\n",cval);printf("rval is %d\n",(int)rval); //返回-1:表示线程被取消return 0;}
1.默认的延时取消结果:
machine:~/Desktop/C/thread$ ./thread_cancel
new thread:set cancel disable
main thread
set cancel enable
main thread
cval is 0
rval is -1
2.设置立即取消的结果:
machine:~/Desktop/C/thread$ ./thread_cancel
new thread:set cancel disable
main thread
main thread
cval is 0
rval is -1
向线程发送信号
pthread_kill
用于向特定线程发送信号以实现线程间通信。
大部分的signal的默认动作是终止进程的运行,所以需要使用sigaaction()去抓信号并加上处理函数。
函数原型:
int pthread_kill(pthread_t thread, int sig);
参数说明:
thread:指定目标线程的ID,该ID必须是通过pthread_create创建的合法线程ID。
sig:要发送的信号值,取值范围为0至63的整数值。特殊值0用于线程存活检测,不发送实际信号。
功能特点:
1.当sig参数不为0时,向指定线程发送信号。若线程未处理该信号,则按信号的默认行为影响整个进程。也就是说,如果你给线程发送了SIGQUIT,但线程没有实现signal处理函数的话,则整个进程退出。
2.当sig参数为0时,仅执行线程有效性检测,不发送实际信号,用于判断线程是否存活。
注意事项:
1.使用pthread_kill发送信号时,需确保线程内已正确实现信号处理函数,否则可能影响整个进程的运行。
2.信号处理函数是进程级全局生效的,不同线程不能定义独立的信号处理逻辑。
3.发送SIGKILL等可能导致进程级终止的信号时,需谨慎处理,以避免资源泄漏或进程异常终止。
应用场景:
1.线程间通信:通过发送信号实现线程间的同步或事件通知。
2.线程状态检测:通过发送0信号判断线程是否存活。
3.异常处理:定向发送异常信号到监控线程,进行异常捕获和处理。