Основные проблемы с исходной версией, о которой идет речь, заключается в том, что ей нужно использовать косвенную адресацию регистра и принимать ссылку (или параметр указателя), а не параметр-значение для блокировки DWORD.
Вот рабочее решение для Visual C ++. РЕДАКТИРОВАТЬ: Я работал в автономном режиме с автором, и мы убедились, что код в этом ответе работает в его тестовом жгуте правильно.
Но если вы используете Windows, вам действительно следует использовать Interlocked API (т.е. InterlockedExchange).
Редактировать: Как отмечает CAF, lock xchg
не требуется, поскольку xchg
автоматически устанавливает BusLock.
Я также добавил более быструю версию, которая выполняет чтение без блокировки, прежде чем пытаться выполнить xchg
. Это значительно уменьшает конкуренцию BusLock на интерфейсе памяти. Алгоритм может быть ускорен совсем немного больше (в спорном случае многопоточного), делая backoffs (выход потом спать) для блокировок долгого времени. В случае однопоточного ЦП наиболее быстрым будет использование блокировки ОС, которая немедленно отключается при удерживаемых блокировках.
class LockImpl
{
// This is a simple SpinLock
// 0 - in use / busy
// 1 - free / available
public:
static void lockResource(volatile DWORD &resourceLock )
{
__asm
{
mov ebx, resourceLock
InUseLoop:
mov eax, 0 ;0=In Use
xchg eax, [ebx]
cmp eax, 0
je InUseLoop
}
}
static void lockResource_FasterVersion(DWORD &resourceLock )
{
__asm
{
mov ebx, resourceLock
InUseLoop:
mov eax, [ebx] ;// Read without BusLock
cmp eax, 0
je InUseLoop ;// Retry Read if Busy
mov eax, 0
xchg eax, [ebx] ;// XCHG with BusLock
cmp eax, 0
je InUseLoop ;// Retry if Busy
}
}
static void unLockResource(volatile DWORD &resourceLock)
{
__asm
{
mov ebx, resourceLock
mov [ebx], 1
}
}
};
// A little testing code here
volatile DWORD aaa=1;
void test()
{
LockImpl::lockResource(aaa);
LockImpl::unLockResource(aaa);
}