Pull to refresh

Thread Locking без дополнительных библиотек на C++

Хочу поделиться с читателями Хабра опытом в разработке многопоточных приложений на C++.

Введение


На днях я столкнулся с проблемой многотопочности. Несколько потоков пытались производить некие манипуляции с буфером либо переменной, и нарушалась либо целостность данных, либо это приводило к некой ошибке. В любом случае это было неприемлемо и нужно было решение.

Я сразу же сел гуглить возможные варианты решения данной проблемы, но ничего толкового найти мне к сожалению не удалось (возможно плохо искал). Кроме варианта использования библиотеки из CRT, но этот вариант мне не подходил так как я полностью отказался от использования CRT в своём проекте.

Реализация


Немного поразмыслив, я вспомнил, как осуществлялась блокировка потоков в C#.

Object locker = new Object();

void Method()
{
    lock(locker)
    {
        // некий код, во время выполнения которого будут
        // приостановлены другие потоки вызвавшие метод Method
    }
}


Ещё немного подумав, я пришёл к идеи реализации блокировки на С++. Был создал класс Lock с 2 методами, 1 инициализацией и 1 переменной.

Файл Lock.h
class Lock
{
public:

	Lock();
	void lock();
	void unlock();

private:

	byte state;

};


Файл Lock.cpp
#include <Windows.h>
#include "Lock.h"

Lock::Lock()
{
	this->state = 0;
}

void Lock::lock()
{
	byte *temp = &this->state;
	_asm
	{
		push eax;
	next:
		mov eax, [temp];
		cmp [eax], 0xFF;
		je next;
		mov [eax], 0xFF;
		pop eax;
	}
}

void Lock::unlock()
{
	byte *temp = &this->state;
	_asm
	{
		push eax;
		mov eax, [temp];
		mov [eax], 0;
		pop eax;
	}
}

Разбор полётов


Сейчас постараюсь популярно объяснить, как работает данный простенький класс.

Чтобы иметь доступ к переменной Lock::state типа byte, создаём ссылку:

byte *temp = &this->state;

Затем помещаем адрес в регистр eax:

push eax;
mov eax, [temp];

В методе Lock::lock() проверяем значение Lock::state, и если оно равно 0xFF, то переходим в «next».

next:
	mov eax, [temp];
	cmp [eax], 0xFF;
	je next;

Если же значение Lock::state не равно 0xFF то меняем на 0xFF и выходим из метода.

mov [eax], 0xFF;
pop eax;

В методе Lock::unlock() всё проще, мы просто изменяем значение Lock::state на 0x00:

push eax;
mov eax, [temp];
mov [eax], 0;
pop eax;

Использование


Использовать данный класс очень просто, достаточно объявить класс Lock и в начале нужного куска кода вызвать метод lock(), для блокировки остальных потоков добравшихся до этого места, а затем обязательно после выполнения кода вызвать метод unlock(), дабы остальные потоки получили возможность спокойно продолжить свою работу.

Lock lock;

void Method() // Метод который вызывается в различных потоках
{
    lock.lock();
    // Код который должен обрабатываться только одним потоком одновременно
    lock.unlock();
}

Вот в принципе и всё. Спасибо за то что уделили немного времени и прочитали данную статью.

P.S. Любая критика приветствуется, я самоучка и многих моментов могу не знать.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.