pthread_atfork函数
函数原型:
int pthread_atfork(void (*prepare)(void), void (*parent)(void),
void (*child)(void));
函数功能
简而言之,该函数在调用**fork()**前执行prepare函数,调用fork()后在子进程执行child函数,在父进程执行parent函数,返回0表示函数执行成功。值得注意的是,无论函数定义在哪里,只有在下一个fork()执行前,该函数才被执行。
线程进程混合引起的死锁
对于一个简单的进程与线程混合代码如下:
代码示例
代码1
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
#include <sys/wait.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *doit(void *arg = NULL)
{
printf("Begin doit with pid %d\n", getpid());
//pthread_mutex_lock(&mutex);
struct timespec t{2,0};
nanosleep(&t, NULL);
//pthread_mutex_unlock(&mutex);
printf("End doit with pid %d\n", getpid());
}
int main()
{
pthread_t t;
pthread_create(&t, NULL, doit, NULL);
printf("sub thread created successfully\n");
struct timespec t2{2,0};
nanosleep(&t2, NULL);
if(fork() == 0)
{
printf("sub process created successfully\n");
doit();
}
wait(NULL);
return 0;
}
执行后的结果为:
从结果来看,没有什么问题,因为并未涉及到临界区,那么下面,我们假设有一个临界区:
代码2
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
#include <sys/wait.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *doit(void *arg = NULL)
{
printf("Begin doit with pid %d\n", getpid());
//pthread_mutex_lock(&mutex);
struct timespec t{2,0};
nanosleep(&t, NULL);
//pthread_mutex_unlock(&mutex);
printf("End doit with pid %d\n", getpid());
}
int main()
{
pthread_t t;
pthread_create(&t, NULL, doit, NULL);
printf("sub thread created successfully\n");
struct timespec t2{2,0};
nanosleep(&t2, NULL);
if(fork() == 0)
{
printf("sub process created successfully\n");
doit();
}
wait(NULL);
return 0;
}
上面就是把那两个注释去掉,运行的结果好像和之前还是一样。下面更近一步,让主函数中等待的时间短一点:
代码3
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
#include <sys/wait.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *doit(void *arg = NULL)
{
printf("Begin doit with pid %d\n", getpid());
//pthread_mutex_lock(&mutex);
struct timespec t{2,0};
nanosleep(&t, NULL);
//pthread_mutex_unlock(&mutex);
printf("End doit with pid %d\n", getpid());
}
int main()
{
pthread_t t;
pthread_create(&t, NULL, doit, NULL);
printf("sub thread created successfully\n");
//更改的是这个地方
struct timespec t2{1,0};
nanosleep(&t2, NULL);
if(fork() == 0)
{
printf("sub process created successfully\n");
doit();
}
wait(NULL);
return 0;
}
结果如下:
输出中并未见到第二个"end doit",这说明代码中有死锁的现象,那么现在分析一下为什么会有死锁的现象。代码2和3中的区别就是等待时间的长短,其中在代码2中,主线程和子线程等待时间一样,主线程等待完毕,子线程执行完毕,代码运行正常,但是安全吗?这个是不安全的,操作系统的异步性不能总是保证二者同时执行完毕或者子线程先于主线程执行完毕。
在代码3中,我们缩短了主线程的等待时间,这种情况下,子线程是处于加锁的状态,创建子进程时,这个状态被复制到子进程中,子进程调用这个函数,重复加锁,因此产生死锁。
解决方案
既然调用的时候可能会产生锁状态复制的情况,那么创建子进程之前解一下锁就行,反正有锁解锁,无锁解一下也不会出错。因此结合pthread_fork函数改正后的代码为:
代码4
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
#include <sys/wait.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *doit(void *arg)
{
printf("Begin doit with pid %d\n", getpid());
pthread_mutex_lock(&mutex);
struct timespec t={2,0};
nanosleep(&t, NULL);
pthread_mutex_unlock(&mutex);
printf("End doit with pid %d\n", getpid());
}
void *prepare(void *arg )
{
pthread_mutex_unlock(&mutex);
}
void *parent(void *arg)
{
pthread_mutex_lock(&mutex);
}
int main()
{
pthread_t t;
pthread_atfork(prepare, parent, NULL);
pthread_create(&t, NULL, doit, NULL);
printf("sub thread created successfully\n");
struct timespec t2={1,0};
nanosleep(&t2, NULL);
if(fork() == 0)
{
printf("sub process created successfully\n");
doit(NULL);
}
wait(NULL);
return 0;
}
代码运行正常