Linux C系统编程-线程取消例程和同步互斥 (三)

线程取消例程函数

当线程收到取消请求时,先不要马上响应取消请求,首先执行一个线程的例程函数,执行完这个函数再响应取消。
一般地,线程取消例程函数里面都是写一些释放公共资源的内容、在Linux系统,公共资源中的互斥锁,条件变量。

使用线程取消例程函数,是为了防止线程带着系统公共资源一起被取消,如果带着系统资源而退出,则其他的线程就无法再次使用该资源。

只需要在线程中调用
pthread_cleanup_push()
功能: push thread cancellation clean-up handlers -> 线程取消例程函数

routine:取消例程函数
arg:传递例程函数的参数
在这里插入图片描述

信号处理函数: void fun(int sig)
线程创建: void *fun(void *arg)
线程取消例程函数: void fun(void *)

将取消例程函数弹栈
void pthread_cleanup_pop(int execute);

execute: 0 -> 不执行例程
非0 -> 执行例程

子线程收到主线程的退出时,不要马上取消,而是先打印"I recv cancel!\n",再取消。

void fun(void *arg)
{
	printf("I recv cancel!\n");
}

void *routine(void *arg)
{
	pthread_cleanup_push(fun,NULL);
	
	int i;
	for(i=0;i<10;i++)
	{
		sleep(1);
		printf("%d\n",i);
	}
	
	pthread_exit(NULL); // 还没来得及删除函数就pthread_exit退出了,那么还是会执行例程函数
	return;   			// 还没来得及删除函数就return退出了,那么不会执行例程函数
	
	sleep(1);
	pthread_cleanup_pop(1);  //正常删除例程函数,如果参数非0  -> 执行
							// 正常删除例程函数,如果是0,  -> 不执行
}

int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,routine,NULL);
	pthread_join(tid,NULL);
	
	return 0;
}

线程同步互斥

使得多个线程之间处理任务时有先后顺序:
有名信号量 对象:进程
无名信号量 对象:线程

有名信号量

创建并打开一个有名信号量
sem_open()
功能: initialize and open a named semaphore -> 初始化并且打开一个有名信号量
使用格式:
在这里插入图片描述

name: 有名信号量的名字,要求是必须以“/”开头 例如: /sem_test
oflag: O_CREAT 不存在则创建 需要额外填写两个参数
O_EXCL 存在则报错
mode: 八进制权限 0777
value: 有名信号量起始值

返回值:
成功: 有名信号量起始地址
失败: SEM_FAILED -1

有名信号量P/V操作
P操作: sem_post() 资源数+1 红灯(0) 绿灯(1)
V操作: sem_wait() 资源数-1 绿灯(1) 红灯(0)
在这里插入图片描述
sem: 有名信号量的地址

关闭有名信号量
sem_close()
功能: sem_close - close a named semaphore
在这里插入图片描述

销毁有名信号量
sem_unlink()
功能: sem_unlink - remove a named semaphore
在这里插入图片描述

name: 有名信号量的名字

Jack进程与Rose进程之间使用共享内存来通信,使用有名信号量来处理同步互斥
Jack:

int main()
{
	//1. 申请key值
	key_t key = ftok(".",10);
	
	//2. 根据key值申请ID号
	int shmid = shmget(key,1024,IPC_CREAT|0666);
	
	//3. 根据ID号申请空间
	char *p = (char *)shmat(shmid,NULL,0);
	
	//4. 创建并且打开一个有名信号量
	sem_t *sem = NULL;
	sem = sem_open("/sem_test",O_CREAT,0777,0);  //说明当前的资源数为0
	
	//5. 不断往共享内存中写入数据
	while(1)
	{
		fgets(p,1024,stdin);  //把车开进车库
		
		sem_post(sem);  //资源数为1
		//0->1
		
		if(strncmp(p,"quit",4) == 0)
		{
			break;
		}
	}	
}

Rose:


int main()
{
	//1. 申请key值
	key_t key = ftok(".",10);
	
	//2. 根据key值申请ID号
	int shmid = shmget(key,1024,IPC_CREAT|0666);
	
	//3. 根据ID号申请空间
	char *p = (char *)shmat(shmid,NULL,0);
	
	//4. 创建并且打开一个有名信号量
	sem_t *sem = NULL;
	sem = sem_open("/sem_test",O_CREAT,0777,0);  //说明当前的资源数为0
	
	//5. 不断往共享内存中写入数据
	while(1)
	{
		sem_wait(sem);  //一直阻塞询问资源能否-1     如果当前资源为1,则函数返回  
												//如果当前资源为0,则函数阻塞
		
		//1->0
		printf("from Jack:%s",p);
		
		if(strncmp(p,"quit",4) == 0)
		{
			break;
		}
	}	
}

无名信号量
对象: 线程
由于无名信号量是无名的,所以说不能打开,但是可以初始化。
sem_init()
功能:初始化无名信号量 initialize an unnamed semaphore在这里插入图片描述
sem: 信号量的地址
pshared: 作用的对象 0 线程
非0 进程
value:信号量的初始化值

有名信号量P/V操作
P操作: sem_post() 资源数+1 红灯(0) 绿灯(1)
V操作: sem_wait() 资源数-1 绿灯(1) 红灯(0)

#include <semaphore.h>

   int sem_post(sem_t *sem);    unlock semaphore

   int sem_wait(sem_t *sem);  -  lock semaphore

sem: 有名信号量的地址

销毁无名信号量
sem_destroy()
功能: sem_destroy - destroy an unnamed semaphore

   int sem_destroy(sem_t *sem);

sem:需要销毁的无名信号量的地址

创建5个线程,每一个线程的任务都是一样
任务: 将"helloworld"字符串1秒打印一个字符 完成任务需要10秒。
要求5个子线程依次打印helloworld,不要同时打印。

void *routine(void *arg)
{
	sem_t *sem = (sem_t *)arg; 
	
	//叉子:1
	
	//谁先能把资源数-1,谁就吃蛋糕!
	sem_wait(sem);
	
	//叉子:0  -> 这时别的线程就没办法抢占了这个资源
	
	int i;
	char buf[] = "helloworld";
	for(i=0;buf[i]!='\0';i++)
	{
		fprintf(stderr,"%c",buf[i]);
		sleep(1);
	}
	
	//吃完蛋糕了,应该将叉子放出来,资源数变成1
	sem_post(sem);
	
	//叉子:1  -> 别的线程就有机会抢占资源。
	
	pthread_exit(NULL);
}

int main()
{
	//1. 初始化无名信号量
	sem_t sem;
	sem_init(&sem,0,1); //当前资源数为0
	
	//2. 产生5个子线程
	pthread_t tid[5]; //0~4
	int i;
	for(i=0;i<5;i++)
	{
		pthread_create(&tid[i],NULL,routine,(void *)&sem);
	}
	
	//3. 回收资源
	for(i=0;i<5;i++)
	{
		pthread_join(tid[i],NULL);
	}
	
	sem_destroy(&sem);
	
	return 0;	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值