线程取消例程函数
当线程收到取消请求时,先不要马上响应取消请求,首先执行一个线程的例程函数,执行完这个函数再响应取消。
一般地,线程取消例程函数里面都是写一些释放公共资源的内容、在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;
}