分叉,同时拿着一把锁

时间:2017-08-25 20:55:49

标签: c++ multithreading

我有以下程序:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>


int main() {
   pthread_mutex_t lock_;
   pthread_mutexattr_t ma;
   pthread_mutexattr_init( &ma );
   pthread_mutexattr_setpshared( &ma, PTHREAD_PROCESS_SHARED );
   pthread_mutexattr_settype( &ma, PTHREAD_MUTEX_ERRORCHECK );
   pthread_mutex_init( &lock_, &ma );

   pthread_mutex_lock( &lock_ );

   if(fork()==0) {
      std::cout << "child" << std::endl;
      pthread_mutex_lock( &lock_ );
      std::cout << "finish" << std::endl;
   } else {
      std::cout << "parent" << std::endl;
      sleep(1);
      pthread_mutex_lock( &lock_ );
      std::cout << "parent done" << std::endl;
   }

}

我看到的行为是父母可以重新锁定互斥锁,而不是孩子。我本以为fork()会分叉当前线程的所有上下文,所以孩子最终会锁定它已经锁定的IE(我不想共享锁 - 两个进程都有自己的锁是我想要的)。为什么这不起作用/如何实现这一目标?

1 个答案:

答案 0 :(得分:4)

它不起作用仅仅因为它以一种有点令人困惑的方式被明确记录为不起作用。 fork()和多线程进程不能很好地协同工作。

虽然fork()手册页最初是通过声称它“通过复制调用过程创建一个新进程”开始的,但这只是一个小小的谎言。如果fork()真正做到并重复整个过程,它必须忠实地复制所有进程的执行线程。因为这就是您的流程的全部内容:构成整个流程的所有执行线程。

但事实并非如此。

如果你keep reading the fork(2) manual page,你将会看到这一部分:

  

子进程是使用单个线程创建的   叫fork()。

这将是你的鸣笛线索#1,fork()并没有真正复制整个过程。它只复制了一个执行线程。

  

父级的整个虚拟地址空间是   复制在孩子身上,包括互斥状态,状况   变量和其他pthreads对象;

现在,停下来思考这意味着什么:只有一个执行线程被分叉,但所有这些都在子进程中被精心复制。我们必须从中得出的结论相当丑陋。

如果您考虑一下:子进程继续执行一个单独的执行线程,其互斥锁最初被其他线程锁定。但是子进程中不存在其他线程。它没有分叉。只分叉了一个线程。

这实际上已在pthread_atfork(3) manual page

中阐明
  

例如,在调用fork(2)时,其他线程可能会   已锁定在用户空间内存中可见的互斥锁   在孩子身上重复。因此,这样的互斥体永远不会被解锁   放置锁的线程不会在子节点中重复。

所以底线,在你的示例代码中,子进程最终在子进程中拿着一个包含一堆不再处于有效状态的互斥锁的包。