我在C中有一个哈希表实现,其中表中的每个位置都是一个链表(用于处理冲突)。这些链表本质上是线程安全的,因此如果表是一个常量大小,则不需要在散列表级别写入额外的线程安全代码 - 散列表是线程安全的。
但是,我希望哈希表在添加值时动态扩展,以便保持合理的访问时间。为了扩展表,它需要额外的线程安全性。
出于这个问题的目的,可以安全地同时发生的过程是“良性的”,并且表调整大小过程(不能同时发生)是“关键的”。当前使用该列表的线程称为“用户”。
我的第一个解决方案是为所有锁定互斥锁的关键功能添加“前导码”和“后置码”代码,然后等待,直到没有当前用户继续进行。然后我将前导码和后同步码添加到良性函数中以检查关键函数是否在等待,如果是,则等待相同的互斥量直到关键部分完成。
在伪代码中,前导/后导函数应该看起来像:
benignPreamble(table) {
if (table->criticalIsRunning) {
waitUntilSignal;
}
incrementUserCount(table);
}
benignPostamble(table) {
decrementUserCount(table);
}
criticalPreamble(table) {
table->criticalIsRunning = YES;
waitUntilZero(table->users);
}
criticalPostamble(table) {
table->criticalIsRunning = NO;
signalCriticalDone();
}
我的实际代码显示在此问题的底部,并使用(可能不必要)caf's PriorityLock from this SO question。坦率地说,我的实施smells awful。处理这种情况的更好方法是什么?目前我正在寻找一种方式来向互斥锁发出信号,表示它是无关紧要的并且同时“解锁所有等待的线程”,但我一直认为必须有一种更简单的方法。我试图以这样的方式对其进行编码:如果关键进程没有运行,任何线程安全机制都会被“忽略”。
当前代码
void startBenign(HashTable *table) {
// Ignores if critical process can't be running (users >= 1)
if (table->users == 0) {
// Blocks if critical process is running
PriorityLockLockLow(&(table->lock));
PriorityLockUnlockLow(&(table->lock));
}
__sync_add_and_fetch(&(table->users), 1);
}
void endBenign(HashTable *table) {
// Decrement user count (baseline is 1)
__sync_sub_and_fetch(&(table->users), 1);
}
int startCritical(HashTable *table) {
// Get the lock
PriorityLockLockHigh(&(table->lock));
// Decrement user count BELOW baseline (1) to hit zero eventually
__sync_sub_and_fetch(&(table->users), 1);
// Wait for all concurrent threads to finish
while (table->users != 0) {
usleep(1);
}
// Once we have zero users (any new ones will be
// held at the lock) we can proceed.
return 0;
}
void endCritical(HashTable *table) {
// Increment back to baseline of 1
__sync_add_and_fetch(&(table->users), 1);
// Unlock
PriorityLockUnlockHigh(&(table->lock));
}
答案 0 :(得分:3)
看起来你正在尝试重新发明读写器锁,我相信pthreads提供了一个原语。你尝试过使用它吗?
更具体地说,你的良性函数应该是一个“读者”锁,而你的关键函数需要一个“作者”锁。最终结果是,许多良性函数可以根据需要执行,但是当关键函数开始执行时,它将等待直到没有良性函数正在进行,并且将阻止其他良性函数直到它完成。我想这就是你想要的。