(1)线程的取消 -----强制使进程退出

不建议使用取消接口来使线程退出

①向线程发送取消请求
    pthread_cancel(pthread_t thread);    
    1.一个线程可以通过调用该函数向另一个线程发送取消请求。
    2.如果成功,该函数返回0,否则将错误码返回。
    3.这不是个阻塞型接口,发出请求后,函数就立刻返回了,而不会等待目标线程退出之后才返回。
    4.函数的本质:会向目标线程发送一个SIGCANCEL(可靠信号)的信号,该信号就是6.4节“ 信号的分类 ”中提到的被NPTL征用的32号信号。

②设置线程能否被取消(在线程执行函数中设置)
    int pthread_setcancelstate(int state, int *oldstate);
    ·PTHREAD_CANCEL_ENABLE 
        线程的默认取消状态是 PTHREAD_CANCEL_ENABLE 
    ·PTHREAD_CANCEL_DISABLE
        1.其他线程发送的SIGCANCEL信号被暂时挂起(而不是被抛弃),暂时不予处理。当线程状态设置为PTHREAD_CANCEL_ENABLE后, SIGCANCEL信号不在被挂起, 会加入到正常的信号队列中。
        2.那么收到取消请求后,会发生什么?这取决于线程的取消类型。    

        
③设置线程的取消类型
    int pthread_setcanceltype(int type, int *oldtype);
    ·PTHREAD_CANCEL_ASYNCHRONOUS(异步取消)    // 永远不要使用异步取消
        即线程可能在任何时间点(可能是立即取消,但也不一定)取消线程。这种取消方式太粗暴,很容易造成后续的混乱。
    
    ·PTHREAD_CANCEL_DEFERRED(延迟取消)
        1.新建线程的默认取消类型为"延迟取消"。
        2.取消点: 就是对于某些函数,如果线程允许取消且取消类型是延迟取消,并且线程也收到了取消请求,
            那么当执行到这些函数的时候,线程就可以退出了。==>这些函数称为"取消点"函数
        3.通过man pthreads可以查询到这些取消点函数(有好几十个之多)。    
        4.线程执行到取消点,会自动处理取消请求,但是如果线程没有用到任何取消点函数。
            1.为了应对这种场景,系统引入了pthread_testcancel函数,该函数一定是取消点。
            2.编程者可以周期性地调用该函数,只要有取消请求,线程就能响应。
            void pthread_testcancel(void);
        5.如果线程被取消,并且其分离状态是可连接的,那么需要由其他线程对其进行连接(回收资源)。
        6. pthread_join 函数的第二个参数会被置成 PTHREAD_CANCELED ,通过该值可以知道线程并不是“寿终正寝”,而是被其他线程取消而导致的退出。

④线程取消的弊端
    1.线程取消是一种在线程的外部强行终止线程的执行做法
    2.目标线程可能会持有互斥量、信号量或其他类型的锁,这时候如果收到取消请求,其他线程可能会死锁(被取消的线程未释放锁)

⑤异步取消安全函数    //即使执行异步取消也安然无恙的函数
    pthread_cancel()
    pthread_setcancelstate()
    pthread_setcanceltype()
    
⑥编程人员应该遵循以下原则:
第一,轻易不要调用 pthread_cancel 函数,在外部杀死线程是很糟糕的做法,毕竟如果想通知目标线程退出,还可以采取其他方法。

第二,如果不得不允许线程取消,那么在某些非常关键不容有失的代码区域,暂时将线程设置成不可取消状态,退出关键区域之后,再恢复成可以取消的状态。
第三,在非关键的区域,也要将线程设置成延迟取消,永远不要设置成异步取消。    


⑦示例代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>

static int CANCEL_POINT = 0;
void *runtime1(void *arg)
{
	printf("runtime1: before\n");
	sleep(1);
	printf("runtime1: after\n");	// 这一句不会执行, 线程被强制取消了
	pthread_exit(NULL);
}
void *runtime2(void *arg)
{	
	/* 设置线程的状态为不可取消 */
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
	
	printf("runtime2: before\n");
	sleep(1);
	printf("runtime2: after\n");	// 这一句不会执行, 线程被强制取消了
	pthread_exit(NULL);
}
void *runtime3(void *arg)
{	

	/* 设置线程的取消类型为"延时取消" */
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); // 永远不要使用异步取消: PTHREAD_CANCEL_ASYNCHRONOUS
	//sleep(1);				// sleep也是一个还原点函数, 所以不能用
	CANCEL_POINT = 0x121;	// 为什么不用printf? 因为printf也是一个还原点函数(执行man pthreads查看所有还原点函数)
	pthread_testcancel();	// 该函数一定是一个还原点函数
	pthread_exit(NULL);
}
void *runtime4(void *arg)
{	

	/* 设置线程的取消类型为"延时取消" */ // 永远不要使用异步取消: PTHREAD_CANCEL_ASYNCHRONOUS
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); 

	/* 编写关键代码, 设置为不允许线程被取消 */
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);	// 这时线程取消信号SIGCANCEL被挂起在队列中
	printf("This is the key code\n"); // printf是一个取消点函数, 但是这个线程不会被取消

	/* 关键代码写完后, 设置为线程允许被取消 */
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

	// 该函数一定是一个还原点函数
	pthread_testcancel();			/* 挂起SIGCANCEL信号被解除, 线程被取消 */
	pthread_exit(NULL);
}
int main(int argc, char **argv)
{
	pthread_t ptid1, ptid2,  ptid3, ptid4;
	void *pret;
	void *pret2;
	void *pret3;
	void *pret4;
printf("\n------------------------1-pthread cancel test--------------------------------\n");
	/* 创建的线程默认状态为允许取消, 取消类型为延迟取消: PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DEFERRED */
	if (pthread_create(&ptid1, NULL, runtime1, NULL))
		perror("pthread_create1 err");
	pthread_cancel(ptid1);		// 发送SIGCANCEL信号,强制取消线程runtime1
	pthread_join(ptid1, &pret);  // 如果线程被取消,并且其分离状态是可连接的(默认),那么需要由其他线程对其进行连接(回收资源)。
	/* 如果线程被取消, 第二个参数被置为PTHREAD_CANCELED */
	if (pret == PTHREAD_CANCELED)
		printf("runtime1 canceled, It's not a normal exit\n");
	else
		printf("runtime1 normal exit\n");
	
printf("\n------------------------2-pthread cannot cancel test--------------------------------\n");
	/* 创建的线程默认状态为允许取消, 取消类型为延迟取消: PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DEFERRED */
	if (pthread_create(&ptid2, NULL, runtime2, NULL))
		perror("pthread_create2 err");
	
	pthread_cancel(ptid2);		// 这个线程不会被取消成功, 因为线程设置了不可取消状态
	pthread_join(ptid2, &pret2);  // 如果线程被取消,并且其分离状态是可连接的(默认),那么需要由其他线程对其进行连接(回收资源)。
	/* 如果线程被取消, 第二个参数被置为PTHREAD_CANCELED */
	if (pret2 == PTHREAD_CANCELED)
		printf("runtime2 canceled, It's not a normal exit\n");
	else
		printf("runtime2 normal exit\n");
	
printf("\n------------------------3-pthread cancel type test--------------------------------\n");
	/* 创建的线程默认状态为允许取消, 取消类型为延迟取消: PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DEFERRED */
	if (pthread_create(&ptid3, NULL, runtime3, NULL))
		perror("pthread_create3 err");
	pthread_cancel(ptid3);		//  这个线程不会立即取消, 直到执行了"取消点"
	pthread_join(ptid3, &pret3);  // 如果线程被取消,并且其分离状态是可连接的(默认),那么需要由其他线程对其进行连接(回收资源)。
	if (CANCEL_POINT == 0x121)
		printf("pthread ptid3 cancel when across CANCEL_POINT\n"); // 线程3直到遇到了取消点,才被取消
	/* 如果线程被取消, 第二个参数被置为PTHREAD_CANCELED */
	if (pret3 == PTHREAD_CANCELED)
		printf("runtime3 canceled, It's not a normal exit\n");
	else
		printf("runtime3 normal exit\n");
	
printf("\n------------------------4- write security code --------------------------------\n");
	/* 创建的线程默认状态为允许取消, 取消类型为延迟取消: PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DEFERRED */
	if (pthread_create(&ptid4, NULL, runtime4, NULL))
		perror("pthread_create4 err");
	pthread_cancel(ptid4);		//  这个线程不会立即取消, 直到执行了"取消点"
	pthread_join(ptid4, &pret4);  // 如果线程被取消,并且其分离状态是可连接的(默认),那么需要由其他线程对其进行连接(回收资源)。
	/* 如果线程被取消, 第二个参数被置为PTHREAD_CANCELED */
	if (pret4 == PTHREAD_CANCELED)
		printf("runtime4 canceled, It's not a normal exit\n");
	else
		printf("runtime4 normal exit\n");
	
	return 0;
}
/* 执行结果:
book@gui_hua_shu:$ gcc pthread_cancel.c -pthread
book@gui_hua_shu:$ ./a.out

------------------------1-pthread cancel test--------------------------------
runtime1: before
runtime1 canceled, It's not a normal exit

------------------------2-pthread cannot cancel test--------------------------------
runtime2: before
runtime2: after
runtime2 normal exit

------------------------3-pthread cancel type test--------------------------------
pthread ptid3 cancel when across CANCEL_POINT
runtime3 canceled, It's not a normal exit

------------------------4- write security code --------------------------------
This is the key code
runtime4 canceled, It's not a normal exit
book@gui_hua_shu:/work/nfs_root/qt_fs_new/2system_pro/pthread$
*/

猜你喜欢

转载自blog.csdn.net/qq_38813056/article/details/85344484