一句话总结:父进程创建子进程时,子进程会复制父进程的内存(包括锁状态),需仔细处理。
1、死锁例子
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* doit(void* arg)
{
printf("pid = %d begin doit ...\n",static_cast<int>(getpid()));
pthread_mutex_lock(&mutex);
struct timespec ts = {2, 0};
nanosleep(&ts, NULL);
pthread_mutex_unlock(&mutex);
printf("pid = %d end doit ...\n",static_cast<int>(getpid()));
return NULL;
}
int main(void)
{
printf("pid = %d Entering main ...\n", static_cast<int>(getpid()));
pthread_t tid;
pthread_create(&tid, NULL, doit, NULL);
struct timespec ts = {1, 0};
nanosleep(&ts, NULL);
if (fork() == 0)
{
doit(NULL);
}
pthread_join(tid, NULL);
printf("pid = %d Exiting main ...\n",static_cast<int>(getpid()));
return 0;
}
运行结果:
通过查询进程可以发现死锁了,子进程3071无法往下执行。
原因:父进程创建的线程调用doit()后,对mutex加了锁;此时fork了一个子进程,子进程复制父进程的内存,包括此时的mutex锁状态;父进程接着往后执行直到结束,而子进程调用了doit(),此时mutex处于加锁状态,一直等待,造成死锁。
2、解决死锁
如何解决上述问题呢,我们需要在fork子进程前将锁释放掉,fork之后再将父进程的锁加上。用
pthread_atfork()在fork()之前调用,当调用fork时,内部创建子进程前在父进程中会调用prepare,内部创建子进程成功后,父进程会调用parent ,子进程会调用child。
不过还是尽量少将多进程多线程混在一起。
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* doit(void* arg)
{
printf("pid = %d begin doit ...\n",static_cast<int>(getpid()));
pthread_mutex_lock(&mutex);
struct timespec ts = {2, 0};
nanosleep(&ts, NULL);
pthread_mutex_unlock(&mutex);
printf("pid = %d end doit ...\n",static_cast<int>(getpid()));
return NULL;
}
void prepare(void)
{
printf("pid = %d prepare ...\n", static_cast<int>(getpid()));
pthread_mutex_unlock(&mutex);
}
void parent(void)
{
printf("pid = %d parent ...\n", static_cast<int>(getpid()));
pthread_mutex_lock(&mutex);
}
void child(void)
{
printf("pid = %d child ...\n", static_cast<int>(getpid()));
}
int main(void)
{
pthread_atfork(prepare, parent, child);
printf("pid = %d Entering main ...\n", static_cast<int>(getpid()));
pthread_t tid;
pthread_create(&tid, NULL, doit, NULL);
struct timespec ts = {1, 0};
nanosleep(&ts, NULL);
if (fork() == 0)
{
doit(NULL);
}
pthread_join(tid, NULL);
printf("pid = %d Exiting main ...\n",static_cast<int>(getpid()));
return 0;
}