pthread类知识点:
- 1、boost::is_same判断两种类型是否是同一类型
- 2、pthread_atfork
函数原型:int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
对应头文件:#include <pthread.h>
用法:调用fork时,内部创建子进程前在父进程中会调用prepare,内部创建子进程成功后,父进程会调用parent ,子进程会调用child
使用示例:
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
void prepare(void)
{
printf("pid = %d prepare ...\n", static_cast<int>(getpid()));
}
void parent(void)
{
printf("pid = %d parent ...\n", static_cast<int>(getpid()));
}
void child(void)
{
printf("pid = %d child ...\n", static_cast<int>(getpid()));
}
int main(void)
{
printf("pid = %d Entering main ...\n", static_cast<int>(getpid()));
pthread_atfork(prepare, parent, child);
//父进程调用parent函数,子进程调用child函数;
//这两个函数的调用顺序不一定
fork();
printf("pid = %d Exiting main ...\n",static_cast<int>(getpid()));
return 0;
}
输出:
为什么要使用?
看一个死锁的例子:
// 一个在多线程程序里fork造成死锁的例子
/*
父进程在创建了一个线程,并对mutex加锁,
父进程创建一个子进程,在子进程中调用doit,由于子进程会复制父进程的内存,
这时候mutex处于锁的状态,
父进程在复制子进程的时候,只会复制当前线程的执行状态,
其它线程不会复制。因此子进程会处于死锁的状态。
*/
#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;
}
输出:
可以看到进程并没有退出,发生死锁了,那么怎样避免这种现象发生?
解除死锁:
#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)//fork之前调用
{
pthread_mutex_unlock(&mutex);
}
void parent(void)//fork成功后,父进程调用
{
pthread_mutex_lock(&mutex);
}
int main(void)
{
pthread_atfork(prepare, parent, NULL);
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;
}
在fork之前先调用pthread_atfork函数解决。
在muduo的thread类中,作者使用该函数作用:
Moduo的thread类中Fork可能是在主线程调用,也可能是在子线程中调用;如果是在子线程中调用的Fork得到一个新进程,新进程只有一个执行序列,(只有一个线程留下来)就是调用fork的线程被继承下来-->这个子线程就是新创建进程的主线程,那么我们需要把这个线程的名称以及tid修改一下。