信号量如何在多核系统上实现?

时间:2016-03-28 13:37:29

标签: c++ multithreading posix semaphore

我无法理解Posix如何允许任何线程在信号量上解锁(post)。让我们考虑以下示例:

// sem1 and sem2 are a Posix semaphore,
// properly initialized for single process use
// at this point, sem2 is locked, sem1 is unlocked 
// x and y are global (non-atomic, non-volatile) integer variables
// thread 1 - is executing right now

rc = sem_wait(&sem1); // succeeded, semaphore is 0 now
x = 42;
y = 142;
sem_post(&sem2);
while (true);

// thread 2. waits for sem2 to be unlocked by thread1 
sem_wait(&sem2);
sem_post(&sem1);

// thread 3
sem_wait(&sem1); // wakes up when sem1 is unlocked by thread2
#ifdef __cplusplus
std::cout << "x: " << x << ; y: " << y << "\n";
#else
printf("x: %d; y: %d\n", x, y);
#endif

现在,根据我读过的所有内容,这段代码是逾越节的100%犹太教徒。在第3个主题中,我们保证x为42,y为142.我们可以从任何种族中受到保护。

但这是我无法理解的。所有这些线程都可以在3个不同的核上执行。如果芯片没有内部强大的内存排序(ARM,PowerPC)或写入不是原子的(x86表示未对齐的数据),Core2上的thread2可能会请求Core1(忙于thread1)来正确释放数据/完整写入/ etc?据我所知,没有这样的命令!

我在这里缺少什么?

编辑。请注意,建议重复不回答我的问题。它重申了我的发言,但没有解释如何实现这种效果。特别是,它没有解释Core2如何在Core1缓存中的数据上设置内存屏障。

3 个答案:

答案 0 :(得分:0)

通过等待来自POSIX Threads Library for Win32的信号(由OS控制)来实现这一点:

       * If the sema is posted between us being cancelled and us locking
       * the sema again above then we need to consume that post but cancel
       * anyway. If we don't get the semaphore we indicate that we're no
       * longer waiting.
       */
      if (*((sem_t *)sem) != NULL && !(WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0))
    {
      ++s->value;
#if defined(NEED_SEM)
      if (s->value > 0)
        {
          s->leftToUnblock = 0;
        }
#else
      /*
       * Don't release the W32 sema, it doesn't need adjustment
       * because it doesn't record the number of waiters.
       */
#endif /* NEED_SEM */
    }
      (void) pthread_mutex_unlock (&s->lock);
    }
}

我们当然没有Windows源代码可供使用。因此,POSIX实现的路径就此结束。

Linux的情况并非如此。 sem_wait.c使用futex_wait(),并且在此函数的源中确定CPU是否可以支持某些功能。例如; CPU有比较和交换功能吗?但即便如此,该架构的全部功能也未得到充分考虑。 lll_futex_wait()中定义了lowlevellock.h等函数。因此,对于PowerPC,我们有powerpc/lowlevellock.h,例如以下代码段:

/* Set *futex to ID if it is 0, atomically.  Returns the old value */
#define __lll_robust_trylock(futex, id) \
  ({ int __val;                                   \
     __asm __volatile ("1:  lwarx   %0,0,%2" MUTEX_HINT_ACQ "\n"          \
               "    cmpwi   0,%0,0\n"                 \
               "    bne 2f\n"                     \
               "    stwcx.  %3,0,%2\n"                \
               "    bne-    1b\n"                     \
               "2:  " __lll_acq_instr                 \
               : "=&r" (__val), "=m" (*futex)                 \
               : "r" (futex), "r" (id), "m" (*futex)              \
               : "cr0", "memory");                    \
     __val;                                   \
  })

所以答案可能是,如果它是在Linux上实现的,那么在架构特定的库中可能存在一些实现或解决方法,这些库可以解决&#39;缺失的问题。指令。

答案 1 :(得分:-1)

如果sem1的初始值= 1且sem2 = 0,则线程3和线程1都可以锁定。它取决于哪个线程首先进入..假设线程3先执行,那么sem1现在将变为0并且线程1不能获取锁定,并且线程2也因为它对线程1的依赖而无法获取锁定。并且如果初始值sem1 = 0然后没有线程可以锁定....我认为这将有所帮助..

答案 2 :(得分:-2)

...因为这就是它的定义方式。

任何线程都可以将单位发布到信号量。

信号量主要不是锁定机制,因此“解锁”是不合适的。

信号量支持发布并等待。

在复杂架构的情况下,例如。许多多核处理器,信号量的实现以及其他线程间同步机制可能会变得相当复杂。例如,可能需要通过处理器间驱动程序发送同步消息,因此硬件中断其他内核以强制它们处理同步。