我们都知道信号量和临界区问题。
在pthread中,可以使用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
如上所述,它会解决临界区问题吗?如果不是,为什么呢?
答案 0 :(得分:3)
使用操作这些对象的pthread_mutex
对象和API而不是编写自己的同步原语的每个人,可能有很多原因。更重要的一个是偶然的:
与许多对广大受众有用的其他对象和功能非常相似,标准化该功能是有意义的,这样人们就不必重新发明轮子,因此他们可以使用和识别标准模式和习语。换句话说,有pthread互斥API,原因与标准字符串操作函数相同。
同步技术非常复杂,难以正确使用。因此,最好有一个经过审查的代码库来执行此功能。即使可以重新发明轮子数百万次,但99%的实施具有严重的缺陷并不是一个好的情况。例如,pthreads处理内存障碍和原子性等问题,这些问题在您的问题中没有正确解决。考虑到问题中的例子:至少有一个严重的问题;它有一个竞争条件,其中两个线程可以同时进入临界区,因为flag
的测试并将其设置为0不是原子地执行。
答案 1 :(得分:2)
首先,如果您的代码有效,第二个线程将完全跳过关键部分。你必须在那里放置一个循环或其他东西。
另外,请考虑调度程序可能在任何地方抢占您的线程这一事实。如果线程A执行测试并且在更改flag
之前被抢占,并且允许线程B进行测试并且之后很快就进入被抢占的关键部分,会发生什么。那里你有两个线程。
答案 2 :(得分:1)
首先,您应该使用原子内存操作(请参阅MSVC中的InterlockedCompareExchange()和GCC中的__sync_val_compare_and_swap())。
其次,这段代码可以正常工作,但只有当第一个线程第一次将标志设置回1时才应该等待。如果它应该,那么你将以循环结束,这将占用你所有的CPU。在这种情况下,你应该使用一些会导致等待线程休眠的东西(例如pthread_mutex_lock())。
答案 3 :(得分:1)
由于您使用“linux”标记了问题,因此可以补充一点,pthreads建立在称为“futexes”或“fast userspace mutexes”之上。正如名称所暗示的那样,快速路径,即锁定和解锁无争用的互斥锁,不需要系统调用,它都在用户空间中完成。 FWIW,AFAIK Windows也做类似的事情。