1

我们都知道信号量和临界区问题。

在 pthreads 中,这可以使用pthread_mutex_lock( )和进行排序pthread_mutex_unlock( )

但是为什么我们需要这些系统调用,当它们可以在代码中实现时,通过执行以下操作:

flag = 0;
if (flag) // Thread1 enters and makes flag = 0
{
   flag = 0; // On entering critical section, flag is made 0 so that others can't enter
  // do some critical section operation
  flag = 1;
}
// Thread1 exits

和上面一样,能解决临界区问题吗?如果不是,那为什么?

4

4 回答 4

3

pthread_mutex使用对象和操作这些对象的 API 而不是每个人都编写自己的同步原语的原因可能有很多。几个更重要的:

  • 就像许多其他对广大受众有用的对象和功能一样,标准化该功能是有意义的,这样人们就不必重新发明轮子,这样他们就可以使用和识别标准模式和习语。换句话说,存在 pthread 互斥 API 的原因与存在标准字符串操作函数的原因相同。

  • 众所周知,同步技术非常复杂且难以正确使用。因此,最好有一个经过审查的代码库来执行此功能。即使重新发明轮子无数次是可以的,但有 99% 的实现存在严重缺陷也不是什么好情况。例如,pthreads 处理诸如内存障碍和原子性之类的问题,这些问题在您的问题中的示例中没有得到正确解决。考虑问题中的示例:至少存在一个严重问题;它有一个竞争条件,两个线程可以同时进入临界区,因为测试flag并将其设置为 0 不是原子执行的。

于 2011-08-19T08:27:30.700 回答
2

首先,如果您的代码可以工作,第二个线程将完全跳过关键部分。你必须在那里放置一个循环或其他东西。

另外,请考虑调度程序可能会在任何地方抢占您的线程这一事实。如果线程 A 进行测试并在更改之前被抢占,flag而线程 B 被允许进行测试并随后很快进入被抢占的临界区,会发生什么情况。你会有两个线程。

于 2011-08-17T07:20:45.813 回答
1

首先,您应该使用原子内存操作(参见 MSVC 中的 InterlockedCompareExchange() 和 GCC 中的 __sync_val_compare_and_swap())。

其次,此代码将起作用,但前提是第二个线程在第一次将标志设置回 1 时不应等待。如果应该等待,您将以循环结束,这将消耗您的所有 CPU。在这种情况下,你应该使用一些东西,这会导致等待线程休眠(例如 pthread_mutex_lock())。

于 2011-08-17T07:26:31.593 回答
1

由于您已将问题标记为“linux”,因此可能会添加 pthreads 构建在称为“futexes”或“快速用户空间互斥体”的东西之上。顾名思义,快速路径,即锁定和解锁非竞争互斥体,不需要系统调用,这一切都在用户空间中完成。FWIW,AFAIK Windows 也做了类似的事情。

于 2011-08-17T08:40:53.570 回答