简单的自定义互斥失败
simple custom made mutex failing
你能发现代码中的错误吗?门票最终低于0,导致长时间的排队。
struct SContext {
volatile unsigned long* mutex;
volatile long* ticket;
volatile bool* done;
};
static unsigned int MyThreadFunc(SContext* ctxt) {
// -- keep going until we signal for thread to close
while(*ctxt->done == false) {
while(*ctxt->ticket) { // while we have tickets waiting
unsigned int lockedaquired = 0;
do {
if(*ctxt->mutex == 0) { // only try if someone doesn't have mutex locked
// -- if the compare and swap doesn't work then the function returns
// -- the value it expects
lockedaquired = InterlockedCompareExchange(ctxt->mutex, 1, 0);
}
} while(lockedaquired != 0); // loop while we didn't aquire lock
// -- enter critical section
// -- grab a ticket
if(*ctxt->ticket > 0);
(*ctxt->ticket)--;
// -- exit critical section
*ctxt->mutex = 0; // release lock
}
}
return 0;
}
等待线程完成的调用函数
for(unsigned int loops = 0; loops < eLoopCount; ++loops) {
*ctxt.ticket = eNumThreads; // let the threads start!
// -- wait for threads to finish
while(*ctxt.ticket != 0)
;
}
done = true;
编辑:这个问题的答案很简单,不幸的是,在我花时间修剪示例以发布简化版本之后,我在发布问题后立即找到了答案。叹息. .
我初始化锁定为0。然后,作为不占用总线带宽的优化,如果互斥锁被占用,我不做CAS。
不幸的是,在这种情况下,当锁被取走时,while循环会让第二个线程通过!
很抱歉我多问了一个问题。我以为我不懂windows低级同步原语,但实际上我只是犯了一个简单的错误。
我在你的代码中看到另一个竞争:一个线程可以导致*ctxt.ticket
达到0,允许父循环返回并重新设置*ctxt.ticket = eNumThreads
而不持有*ctxt.mutex
。其他线程现在可能已经持有互斥锁(事实上,它很可能持有)并对*ctxt.ticket
进行操作。对于您的简化示例,这只能防止"批"被清晰地分开,但如果在loops
循环的顶部有更复杂的初始化(如比单个单词写入更复杂),您可能会看到奇怪的行为。
我发布了一个错误,我认为这是一个合法的多线程问题,但实际上它只是坏的逻辑。我一发帖就解决了这个bug。以下是问题和答案
unsigned int lockedaquired = 0;
我将lockarequired初始化为0,然后添加if语句以跳过执行CAS的昂贵操作。这种优化使它脱离while循环并进入临界区。将代码更改为
unsigned int lockedaquired = 1;
修复问题。在我发现的代码中还有另一个隐藏的问题(我真的不应该再在深夜编码了)。有人注意到临界区if语句后面的分号吗?叹息…
if(*ctxt->ticket > 0);
(*ctxt->ticket)--;
应该是
if(*ctxt->ticket > 0)
另外,Ben Jackson指出,当我们将票证重置为eNumThreads时,一个线程可能会在临界区内。虽然这在这个示例代码中是完美的,但如果你要将它应用到需要执行更多操作的问题中,它可能不安全,因为线程不是同步运行的,所以如果你将它应用到你的代码中,请记住这一点。
最后注意,如果有人决定使用这段代码来实现互斥锁,请记住你的主驱动线程是空闲的。如果您在临界区执行大量操作,这需要花费大量时间,并且您的票数很高,请考虑放弃您的线程,让其他软件在等待期间使用CPU。另外,如果临界区很大,请考虑使用自旋锁。谢谢
- 自定义对象的dlib序列化在gcc中失败
- 使用自定义访问者时具有自定义类型的提升变体失败(源自 boost::static_visitor)
- 自定义预期失败的完整错误消息(boost::spirit::x3)
- std::p作为 std 中定义的unordered_map自定义键的字符串的空气失败并出现模板错误
- QStyledItemDelegate 绘图自定义构件失败
- 使用自定义删除程序返回unique_ptr的 nullptr 失败
- 标准::映射的自定义分配器失败
- 从字符串流读取和写入自定义对象时测试失败
- C++ - 如果自定义提取器失败,如何恢复 istream
- 与自定义命名空间的链接失败未解析的外部符号
- 自定义矢量类模板初始化程序失败
- 从istream读取自定义类型失败时的预期行为
- 从自定义构建的Python导入自定义模块失败
- 创建自定义简单Mapi DLL,执行时失败
- 自定义类的Static_cast会导致复制赋值失败
- 当显式调用非默认函数时,覆盖自定义字符串失败
- 从自定义sqrt函数捕获异常在c++中失败
- Google Mock:如何配置自定义消息来解释匹配失败
- 调试断言失败与字符串流::灌输和自定义全局运算符新
- 简单的自定义互斥失败