克隆(2)(C/C+;+;)可以使用哪些同步原语? < >当使用Linux的克隆(2)线程时,我可以使用什么C++同步原语?我特别不能使用pthreads,因为我正在构建一个共享库,用不同的定义替换pthreads的许多函数调用,但我需要某种类型的互斥

克隆(2)(C/C+;+;)可以使用哪些同步原语? < >当使用Linux的克隆(2)线程时,我可以使用什么C++同步原语?我特别不能使用pthreads,因为我正在构建一个共享库,用不同的定义替换pthreads的许多函数调用,但我需要某种类型的互斥,c++,c,linux,multithreading,C++,C,Linux,Multithreading,编辑:我可能说得太快了,我查看了pthread文档,它们使用futex(2)来实现这些原语。我假设我也会这样做?您可以使用futex 以下是基于futex的简单互斥和cond-var,取决于您对同步工具的要求,您也可以使用原子操作。例如,gcc的\uuuuu sync\u lock\u test\u和\u set可轻松用于自旋锁。这种避免系统调用的用户空间锁在许多情况下比基于内核的解决方案更有效 编辑:如果您只有一些说明需要保护,则会出现这种情况。那么,持有自旋锁的线程在持有该锁时被中断的概率非

编辑:我可能说得太快了,我查看了pthread文档,它们使用futex(2)来实现这些原语。我假设我也会这样做?

您可以使用
futex


以下是基于futex的简单互斥和cond-var,取决于您对同步工具的要求,您也可以使用原子操作。例如,gcc的
\uuuuu sync\u lock\u test\u和\u set
可轻松用于自旋锁。这种避免系统调用的用户空间锁在许多情况下比基于内核的解决方案更有效


编辑:如果您只有一些说明需要保护,则会出现这种情况。那么,持有自旋锁的线程在持有该锁时被中断的概率非常小。如果发生这种情况,等待时间将是一些调度周期,而在现代系统上,这对于实时系统来说应该是不可能的。对于平均等待时间而言,这应该可以忽略不计。

Ulrich Drepper的论文《futex很棘手》中描述了基于futex的互斥器的正确实现。osgx在评论中给出的链接已经过时。当前版本如下:

它不仅包括代码,而且还非常详细地解释了为什么它是正确的。文件中的代码:

class mutex
{
 public:
 mutex () : val (0) { }
 void lock () {
   int c;
   if ((c = cmpxchg (val, 0, 1)) != 0)
     do {
       if (c == 2 || cmpxchg (val, 1, 2) != 0)
         futex_wait (&val, 2);
     } while ((c = cmpxchg (val, 0, 2)) != 0);
 }
 void unlock () {
   if (atomic_dec (val) != 1) {
     val = 0;
     futex_wake (&val, 1);
   }
 }
 private:
   int val;
};
cmpxchg()和atomic_dec()可以分别通过uu sync_val_compare_和_swap()以及u sync_fetch_和_add()实现,它们不是标准的C,但也受GCC和AFAIK by CLANG的支持

请注意,futex()是特定于Linux的。FreeBSD有一个模拟AFAIK,但我不知道您是否可以通过本机访问它。如果要针对Linux以外的其他平台,则不能使用futex()。但是,因为您使用的是特定于Linux的clone(),所以您可能不在乎

关于编写pthreads替换程序的一般想法:

算了吧

我自己走这条路是因为我想让clone()提供一些行为,而不是pthread_create()。过了一会儿,我放弃了。glibc是在假设您正在使用pthreads的基础上构建的。即使(尤其是)如果不链接-lpthread,glibc也包含pthread特定的行为。让我放弃克隆()的原因是错误。在多线程应用程序中,您必须提供线程本地errno,除非您希望使用全局互斥来同步每个libc调用。您可以通过实现_errno_location()来实现这一点。不幸的是,glibc在某些地方有自己的_errno_位置硬编码,不会使用您的替代品

换句话说:如果你想使用你自己的线程库,你不能使用glibc,至少在没有阅读你使用的每一个函数的源代码并准备在必要时替换它之前是这样。如果你想改用uClibc,我会让你失望的。他们从glibc复制了部分实现,至少旧版本存在上述位置问题

我的解决方案是放弃与pthreads的斗争。现在我使用pthRead SkEATE()来创建我的线程(当然,还有一个漂亮的C++包装器)。虽然我还没有尝试过,但unshare(2)系统调用应该允许我更改我希望通过clone()设置的线程的那些方面,而pthread_create()不支持这些方面。如果这不起作用,我将使用pthread_create()的glibc源代码,并将我的选项修改为对clone()的调用,保持其余的相同,这样我就不会破坏兼容性


至于同步原语:使用pthread_create()并不强迫您使用pthread_互斥体和company。因为我正在编写一个VM,其中每个对象都分配了一个互斥体,所以我不希望pthread\u mutex\t的空间开销。相反,我使用的是上面文章中Drepper的互斥类。您可以自由地混合和匹配pthread的原语和您自己的原语。只要可能,您就应该使用pthread版本,因为它们经过了良好的测试。但对于特殊情况(如超轻互斥),可以创建自己的基于futex的原语。

手册页一直提到
进程
——这是否也适用于
线程
?futex是通用的(进程和线程)。NPTL(glibc>=2.3的pthreads库)在内部使用futex来实现几乎所有东西:互斥锁、屏障、condvar……但当只使用原子操作时,并不是每种类型的同步都容易实现或有效。有时需要操作系统辅助功能,
futex
是原子操作系统的良好补充(实际上NPTL同时使用原子和futex,典型的裸futex使用也将其与原子结合起来)@osgx,当然。我只想反对这样一种观点,即您必须通过系统调用从操作系统内核获得帮助,以实现互斥。如果您只有一些要保护的指令,那么就应该使用自旋锁。只有当您有好的调度程序(或没有调度程序)并且M CPU和NM上只有N个线程,或者同一CPU上可以规划2个线程时,自旋锁才会彼此一致,同步原语的速度将与调度程序的滴答声一样慢(1ms)。因此,在一些罕见的情况下,同步操作编写者需要操作系统的帮助才能得到有效和通用的实现。本文描述了如何使用futex构建一些同步原语,最后一个“take”在NPTL中得到了实际应用。