我有一些代码可以锁定(某个库的)每个函数,并且我想对其进行优化。给定功能A
和B
,我不介意与任何其他A
同时运行的A
或与任何其他{{ 1}},但在运行任何B
时都无法运行B
,反之亦然。线程计数是动态的,由于无法控制的原因,我不得不为互斥量和条件变量(即A
)使用静态分配。
我预感最有效的方法是两个条件变量。使用B
允许一个函数(即PTHREAD_MUTEX_INITIALIZER
)并行运行,而另一个函数必须序列化。静态初始化的pthread_mutex_trylock
实际上也是未定义的行为...
编辑:
也许是这样吗?我不确定是否:
A
答案 0 :(得分:1)
您的解决方案具有竞争条件。考虑一下countA
和countB
均为零,并且两个线程同时调用A()
和B()
的情况。第一个线程锁lockB
,第二个线程锁lockA
都将其检查的计数视为零,然后继续增加其各自的计数并出错。
解决方案中的另一个问题是它使用pthread_cond_signal()
并不一定要唤醒一个以上的等待线程,因此,如果有10个线程正在等待输入B()
,而只有一个线程正在运行{{ 1}},当后一个线程完成时,只能保证一个A()
线程继续执行,另外9个线程可以无限期等待。
由于B()
被保留在该调用上,因此它不允许多个线程同时运行doA()
。
要解决第一个问题,可以使用一个保护lockB
和countA
的互斥量(因为我们必须检查共享状态,这两个变量都组合在一起)。同时,您也可能只使用一个条件变量:等待条件变量的线程必须要么全部等待输入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()
这可以防止饥饿,因为:
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
上有任何线程在等待; B()
非零,但是没有线程可以递增queuedB
,因此countA
必须最终达到零,并允许等待countA
的线程继续进行。 / li>
cond
为零时,没有线程可以递增countA
,因此queuedB
必须最终达到零,并允许等待queuedB
的线程继续进行。