线程分离属性,线程取消(状态,类型),线程取消例程函数

目录

1、线程分离属性

2、线程取消(状态,类型)

3、线程取消例程函数

1、线程分离属性

默认情况下,线程启动后处于可接合状态(即未分离),此时的线程可以在退出时让其他线程接合以便释放资源,但若其他线程未及时调用 pthread_join() 去接合它,它将成为僵尸线程,浪费系统资源。

因此,若线程退出时无需汇报其退出值,则一般要设置为分离状态,处于分离状态下的线程在退出之后,会自动释放其占用的系统资源。

将线程设置为分离状态有两种方式:

  • 在线程启动前,使用分离属性启动线程
  • 在线程启动后,使用 pthread_detach() 强制分离

线程分离方法一

方法1:添加一个分离属性到一个属性变量中,然后使用属性变量去创建一个线程, 那么创建出来的线程就是具有分离属性的线程

1)定义一个属性变量 -> 数据类型:pthread_attr_t

pthread_attr_t attr;

2)初始化属性变量。 -> pthread_attr_init() -> man 3 pthread_attr_init

#include <pthread.h>

int pthread_attr_init(pthread_attr_t *attr);

参数: attr:未初始化的属性变量

返回值: 成功:0 失败:非0错误码

3)设置分离属性到属性变量中。

#include <pthread.h>

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

参数: attr:已经初始化过的属性变量

detachstate: PTHREAD_CREATE_DETACHED -> 分离属性 PTHREAD_CREATE_JOINABLE -> 非分离属性(默认状态)

返回值: 成功:0 失败:非0错误码

4)使用属性变量去创建一个新的线程。

pthread_create(&tid,&attr,.....); -> 创建出来的线程就是分离属性的线程,不需要pthread_join()

5)销毁属性变量。 -> pthread_attr_destroy() -> man 3 pthread_attr_destroy int pthread_attr_destroy(pthread_attr_t *attr);

参数: attr:已经初始化过的属性变量

返回值: 成功:0 失败:非0错误码

线程分离方法二

方法2:

先创建一个普通线程,然后在线程中调用一个设置分离属性的函数,那么这个线程就变成分离的属性

1)设置线程本身的属性为分离属性。 -> pthread_detach() -> man 3 pthread_detach #include <pthread.h>

int pthread_detach(pthread_t thread);

函数作用: 设置分离属性给线程

参数: thread:需要设置分离属性的线程的ID号

返回值: 成功:0 失败:非0错误码

2)获取线程的ID号。 -> pthread_self() -> man 3 pthread_self

#include <pthread.h> pthread_t pthread_self(void);

参数: 无

返回值:线程的ID号

pthread_detach(pthread_self())

总结: 无论是否添加了分离属性的线程,理论上创建的线程的数量没有限制。 用两种方法设置分离属性的子线程,主线程都无法结合成功

测试代码如下:

#include<stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

void* start_routine(void *arg)
{
	//pthread_detach(pthread_self());
	printf("[%lu]start_routine\n",pthread_self()); //pthread_self打印当前线程的ID号
	sleep(3);
}

int main()
{
	//方法一
	//1、定义一个 线程属性变量
	pthread_attr_t attr;

	//2、初始化清空 属性变量
	pthread_attr_init(&attr);

	//3、把分离属性 加入 到 属性变量中
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

	//4、创建一条具有分离属性的子线程
	pthread_t thread;
	pthread_create(&thread,&attr,start_routine,NULL);
	
	//方法二
	//pthread_create(&thread,NULL,start_routine,NULL);
	
	//5、销毁属性变量
	pthread_attr_destroy(&attr);

	//阻塞等待子线程退出,回收资源
	int ret = pthread_join(thread,NULL);
	if(ret != 0)
    {
		printf("pthread_join error\n");
	}
}

2、线程取消(状态,类型)

一般主线程不用于去处理任务,只是控制子线程状态,例如取消接合

主线程 ---> 取消请求 ---> 子线程 pthread_cancel() ---> man 3 pthread_cancel #include<pthread.h>

int pthread_cancel(pthread_t thread);

函数作用: 发送一个取消请求给子线程。

参数: thread:需要取消的线程的ID号。

返回值: 成功:0 失败:非0错误码

注意:线程收到取消请求,就等价于提前退出

pthread_exit() -> 是线程主动退出 ->退出值-> pthread_join()

主线程给子线程发送取消请求--->子线程收到取消请求-> 线程被迫退出(被动退出) -> 没有退出值

pthread_setcancelstate() -> man 3 pthread_setcancelstate

#include <pthread.h>

int pthread_setcancelstate(int state, int *oldstate);

参数:

state: PTHREAD_CANCEL_ENABLE -> 能响应 -> 线程默认属性 -> 是马上响应,还是延迟响应 -> 取决于type。

PTHREAD_CANCEL_DISABLE -> 不能响应

oldstate:保留之前的状态,如果不关心,则填NULL。

返回值: 成功:0 失败:非0错误码。

说明: pthread_setcancelstate一般使用在子线程中

If a thread has disabled cancellation, then a cancellation request remains queued until the thread enables cancellation.

//如果一个线程不能响应取消的,那么在这个过程中收到了取消请求,那么这个请求会直到这个线程能响应取消请求为止才会被响应。

If a cancellation request is received, it is blocked until cancelability is enabled.

//如果收到取消请求,那么就会阻塞到这个线程能响应为止。

设置能响应时,子线程收到主线程的取消请求后,立即响应退出线程

设置不能响应时,子线程收到主线程的取消请求后,会阻塞(但取消请求还在),等待可以响应的时候才会响应取消

pthread_setcanceltype() -> man 3 pthread_setcanceltype

#include <pthread.h>

int pthread_setcanceltype(int type, int *oldtype);

参数: type: PTHREAD_CANCEL_DEFERRED -> 延迟取消 -> 遇到一个取消点函数才会响应取消-> 线程默认属性

PTHREAD_CANCEL_ASYNCHRONOUS -> 立即响应

oldtype:保存之前的状态,如果不关心,则填NULL。

注意: 不设置取消线程的话线程是遇到取消点函数之后才会响应取消的,设置后会立即响应退出 取消点函数有哪些呢?

-> man 7 pthreads printf() fprintf() sleep() usleep() fputc() fputs()

注意:主线程给子线程 发送线程取消 ,但是子线程在执行过程中没有遇到取消点函数,所以不能响应取消请求

现象:子线程收到主线程的取消请求后,立即响应退出线程

测试代码如下:

//pthread_cancel.c
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

int p1 = 250;

void* fun1(void* arg)
{
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
	
	int cnt = 0;
	while(1)
	{
		printf("[%s][%d]cnt = %d\n",__FUNCTION__,__LINE__,cnt++);
		sleep(1);
		if(cnt == 10)
		{
			pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
		}
	}
	
	// while(1)
	// {
		// printf("[%s][%d]\n",__FUNCTION__,__LINE__);
		// sleep(1);
	// }
	
	pthread_exit(&p1);
}


int main()
{
	int ret = 0;
	pthread_t pid1;
	ret = pthread_create(&pid1,NULL,fun1,NULL);
	if(ret != 0)
	{
		printf("pthread_create fail\n");
		return -1;
	}
	printf("pthread_create ok\n");
	
	int cnt = 1;
	while(cnt++)
	{
		printf("[%s][%d]	%d\n",__FUNCTION__,__LINE__,cnt);
		sleep(1);
		if(cnt == 5)
		{
			pthread_cancel(pid1);
			break;
		}
	}
		
	void* p = NULL;
	ret = pthread_join(pid1,&p);
	//printf("p = %d\n",*(int*)p);
	
	return 0;
}


//pthread_setcancelstate.c
#include<stdio.h>
#include<pthread.h>
#include <unistd.h>

int pthread_status = 20;

//线程例程,创建一条线程之后,去执行这个函数
void* start_routine(void *arg)
{
    //子线程设置不响应取消请求 
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    
    int cnt=0;
    while(1)
    {
        sleep(1);
        printf("start_routine:%lu cnt:%d\n",pthread_self(),cnt++); //pthread_self 打印自己的线程ID号
        
        if(cnt == 10)
        {
            //子线程设置 取消请求能响应 
            pthread_setcancelstate(PTHREAD_CANCEL_ENABLE  , NULL);
        }
    }
} 

int main()
{
    //创建一条子线程
    pthread_t thread;
    pthread_create(&thread,NULL,start_routine,NULL);
    
    sleep(2);
    //给子线程发送一个取消请求
    pthread_cancel(thread);

    //等待子线程退出
    pthread_join(thread,NULL);
    
    return 0;
}

//pthread_setcanceltype.c
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

//线程例程,创建一条线程之后,去执行这个函数
void* start_routine(void *arg)
{   
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
	//pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);	
	
    while(1)
    {
        //printf("start_routine\n");//取消点函数
        //sleep(1); //取消点函数

    }
} 

int main()
{
    //创建一条子线程
    pthread_t thread;
    pthread_create(&thread,NULL,start_routine,NULL);
    
    //给子线程发送一个取消请求
    pthread_cancel(thread);

    //等待子线程退出
    pthread_join(thread,NULL);
    
    return 0;
}

3、线程取消例程函数

1、什么是线程取消例程函数

当线程收到取消请求时,先不要马上响应取消请求,而是执行一个例程函数先,执行完这个函数再响应取消。

2.为什么要使用取消例程函数

为了防止线程带着一些公共资源而被取消掉,如果带着资源来退出,那么其他线程无法再次使用该资源

1)压栈线程的取消例程函数-> pthread_cleanup_push()

#include <pthread.h>

void pthread_cleanup_push(void (*routine)(void *),void *arg);

参数: routine: 线程的取消例程函数 -> 必须是: void fun(void *arg) arg:传递给取消例程函数的参数

2)弹栈线程的取消例程函数 --> pthread_cleanup_pop()

#include <pthread.h>

void pthread_cleanup_pop(int execute);

参数: execute: 0 -> 删除时,直接删除。 非0 -> 删除时,会先执行一遍例程函数,再删除。

注意:

>1.子线程收到取消请求之后,(遇到取消点函数)就会执行线程取消例程函数,然后执行完就响应取消请求直接退出,不会再往下面执行了 。

>2.如果子线程没有收到取消请求,而且程序执行到pthread_cleanup_pop该函数时,此函数才会执行, 并且根据参数决定是否执行线程取消例程函数再退出子线程。

>3.这两个函数都必须是成对出现的,如果只写一个直接编译会报错

现象:子线程收到取消请求之后,立即执行取消例程函数。pop后面的函数不执行

线程取消例程函数的调用:收到取消请求或者pthread_cleanup_pop()函数的参数值不为0

测试代码如下:

#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
void routine(void *arg)
{
    printf("线程取消例程\n");
}

//线程例程,创建一条线程之后,去执行这个函数
void* start_routine(void *arg)
{
	int time = 5;
    //压栈
    pthread_cleanup_push(routine,NULL);
    
    while(time--)
    {
        printf("start_routine\n");
        sleep(1);
    }
    //弹栈
    pthread_cleanup_pop(1);//0不执行 非0才执行
} 

int main()
{
    //创建一条子线程
    pthread_t thread;
    pthread_create(&thread,NULL,start_routine,NULL);
    
    sleep(5);
    //给子线程发送一个取消请求
    pthread_cancel(thread);

    //等待子线程退出
    pthread_join(thread,NULL);
    
    return 0;
}

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值