pthreads:互斥+并发函数(反信号量)

时间:2018-06-21 19:09:19

标签: c pthreads mutex semaphore

我有一些代码可以锁定(某个库的)每个函数,并且我想对其进行优化。给定功能AB,我不介意与任何其他A同时运行的A或与任何其他{{ 1}},但在运行任何B时都无法运行B,反之亦然。线程计数是动态的,由于无法控制的原因,我不得不为互斥量和条件变量(即A)使用静态分配。

我预感最有效的方法是两个条件变量。使用B允许一个函数(即PTHREAD_MUTEX_INITIALIZER)并行运行,而另一个函数必须序列化。静态初始化的pthread_mutex_trylock实际上也是未定义的行为...

编辑

也许是这样吗?我不确定是否:

  • 可以更简单。毕竟,互斥锁是使用信号量实现的,但是要实现基本上是逆信号量,则需要四个互斥量和两个条件变量。
  • 涵盖所有比赛条件。
  • “合理”(超出默认优先级和计划)吗?

A

1 个答案:

答案 0 :(得分:1)

您的解决方案具有竞争条件。考虑一下countAcountB均为零,并且两个线程同时调用A()B()的情况。第一个线程锁lockB,第二个线程锁lockA都将其检查的计数视为零,然后继续增加其各自的计数并出错。

解决方案中的另一个问题是它使用pthread_cond_signal()并不一定要唤醒一个以上的等待线程,因此,如果有10个线程正在等待输入B(),而只有一个线程正在运行{{ 1}},当后一个线程完成时,只能保证一个A()线程继续执行,另外9个线程可以无限期等待。

由于B()被保留在该调用上,因此它不允许多个线程同时运行doA()

要解决第一个问题,可以使用一个保护lockBcountA的互斥量(因为我们必须检查共享状态,这两个变量都组合在一起)。同时,您也可能只使用一个条件变量:等待条件变量的线程必须要么全部等待输入countB,要么全部等待输入A(),但要混合使用两者是不可能的。解决第二个问题只需要B()。这导致简单得多:

pthread_cond_broadcast()

此解决方案是正确的,但不是“公平的”-如果有连续的线程流在执行static int countA = 0; static int countB = 0; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; void A(void) { pthread_mutex_lock(&lock); while (countB) pthread_cond_wait(&cond, &lock); countA++; pthread_mutex_unlock(&lock); do_A(); pthread_mutex_lock(&lock); countA--; if (!countA) pthread_cond_broadcast(&cond); pthread_mutex_unlock(&lock); } (例如,新线程进入并递增A(),则在前一个线程完成之前,递减),然后等待执行countA的线程将永远等待。在您的特定用途中,这可能不是问题-例如,如果您知道执行B()的任何线程最终都将执行A(),则饥饿情况最终必须解决。

可以通过在有线程排队进入B()时阻止进入A()的新条目来改善系统,以防止这种饥饿:

B()

这可以防止饥饿,因为:

  1. static int countA = 0; static int countB = 0; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static int queuedA = 0; static int queuedB = 0; static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER; void A(void) { pthread_mutex_lock(&lock); while (queuedB) pthread_cond_wait(&queue_cond, &lock); while (countB) { queuedA++; pthread_cond_wait(&cond, &lock); queuedA--; } if (!queuedA) pthread_cond_broadcast(&queue_cond); countA++; pthread_mutex_unlock(&lock); do_A(); pthread_mutex_lock(&lock); countA--; if (!countA) pthread_cond_broadcast(&cond); pthread_mutex_unlock(&lock); } 总是非零,而queuedB中的cond上有任何线程在等待;
  2. 尽管B()非零,但是没有线程可以递增queuedB,因此countA必须最终达到零,并允许等待countA的线程继续进行。 / li>
  3. cond为零时,没有线程可以递增countA,因此queuedB必须最终达到零,并允许等待queuedB的线程继续进行。