我使用Windows中的Interlocked函数制作了一个非常简单的自旋锁,并在双核CPU上测试它(两个线程增加一个变量);
该程序似乎工作正常(每次都给出相同的结果,但不使用同步时不是这种情况),但 Intel Parallel Inspector 表示存在竞争条件< em> value + = j (参见下面的代码)。使用Critical Sections而不是SpinLock时,警告消失。
我的SpinLock实现是否正确?这真的很奇怪,因为所有使用过的操作都是原子的,并且有适当的内存屏障,不应该导致竞争条件。
class SpinLock
{
int *lockValue;
SpinLock(int *value) : lockValue(value) { }
void Lock() {
while(InterlockedCompareExchange((volatile LONG*)lockValue, 1, 0) != 0) {
WaitABit();
}
}
void Unlock() { InterlockedExchange((volatile LONG*)lockValue, 0); }
};
测试程序:
static const int THREADS = 2;
HANDLE completedEvents[THREADS];
int value = 0;
int lock = 0; // Global.
DWORD WINAPI TestThread(void *param) {
HANDLE completed = (HANDLE)param;
SpinLock testLock(&lock);
for(int i = 0;i < 1000*20; i++) {
for(int j = 0;j < 10*10; j++) {
// Add something to the variable.
testLock.Lock();
value += j;
testLock.Unlock();
}
}
SetEvent(completed);
}
int main() {
for(int i = 0; i < THREADS; i++) {
completedEvents[i] = CreateEvent(NULL, true, false, NULL);
}
for(int i = 0; i < THREADS; i++) {
DWORD id;
CreateThread(NULL, 0, TestThread, completedEvents[i], 0, &id);
}
WaitForMultipleObjects(THREADS, completedEvents, true, INFINITE);
cout<<value;
}
答案 0 :(得分:4)
Parallel data race的并行检查器文档建议使用关键部分或互斥锁来修复Windows上的比赛。其中没有任何内容表明Parallel Inspector知道如何识别您可能发明的任何其他锁定机制。
用于分析新型锁定机制的工具往往是静态工具,它们查看代码中的每条可能路径,Parallel Inspector的文档暗示它只执行一次代码。
如果你想试验新的锁定机制,我在学术文献中看到的最常见的工具是Spin model checker。还有ESP,这可能会减少状态空间,但我不知道它是否已经应用于并发问题,还有mobility workbench如果你可以在pi中解决你的问题就可以进行分析-结石。英特尔Parallel Inspector看起来并不像这些工具那么复杂,而是设计用于使用启发式检查常见问题。
答案 1 :(得分:2)
对于处于类似情况的其他穷人:英特尔提供了一套用于执行此类操作的包含和库。检查Inspector安装目录(您将在安装目录中看到\ include,\ lib32和\ lib64)以获取这些材料。关于如何使用它们的文档(截至2018年6月,尽管英特尔不关心保持链接的一致性):
https://software.intel.com/en-us/inspector-user-guide-windows-apis-for-custom-synchronization
有3个功能:
void __itt_sync_acquired(void *addr)
void __itt_sync_releasing(void *addr)
void __itt_sync_destroy(void *addr)
答案 2 :(得分:1)
我很确定它应该如下实现:
class SpinLock
{
long lockValue;
SpinLock(long value) : lockValue(value) { }
void Lock() {
while(InterlockedCompareExchange(&lockValue, 1, 0) != 0) {
WaitABit();
}
}
void Unlock() { InterlockedExchange(&lockValue, 0); }
};