Хочу поделиться с читателями Хабра опытом в разработке многопоточных приложений на C++.
На днях я столкнулся с проблемой многотопочности. Несколько потоков пытались производить некие манипуляции с буфером либо переменной, и нарушалась либо целостность данных, либо это приводило к некой ошибке. В любом случае это было неприемлемо и нужно было решение.
Я сразу же сел гуглить возможные варианты решения данной проблемы, но ничего толкового найти мне к сожалению не удалось (возможно плохо искал). Кроме варианта использования библиотеки из CRT, но этот вариант мне не подходил так как я полностью отказался от использования CRT в своём проекте.
Немного поразмыслив, я вспомнил, как осуществлялась блокировка потоков в C#.
Ещё немного подумав, я пришёл к идеи реализации блокировки на С++. Был создал класс Lock с 2 методами, 1 инициализацией и 1 переменной.
Файл Lock.h
Файл Lock.cpp
Сейчас постараюсь популярно объяснить, как работает данный простенький класс.
Чтобы иметь доступ к переменной Lock::state типа byte, создаём ссылку:
Затем помещаем адрес в регистр eax:
В методе Lock::lock() проверяем значение Lock::state, и если оно равно 0xFF, то переходим в «next».
Если же значение Lock::state не равно 0xFF то меняем на 0xFF и выходим из метода.
В методе Lock::unlock() всё проще, мы просто изменяем значение Lock::state на 0x00:
Использовать данный класс очень просто, достаточно объявить класс Lock и в начале нужного куска кода вызвать метод lock(), для блокировки остальных потоков добравшихся до этого места, а затем обязательно после выполнения кода вызвать метод unlock(), дабы остальные потоки получили возможность спокойно продолжить свою работу.
Вот в принципе и всё. Спасибо за то что уделили немного времени и прочитали данную статью.
P.S. Любая критика приветствуется, я самоучка и многих моментов могу не знать.
Введение
На днях я столкнулся с проблемой многотопочности. Несколько потоков пытались производить некие манипуляции с буфером либо переменной, и нарушалась либо целостность данных, либо это приводило к некой ошибке. В любом случае это было неприемлемо и нужно было решение.
Я сразу же сел гуглить возможные варианты решения данной проблемы, но ничего толкового найти мне к сожалению не удалось (возможно плохо искал). Кроме варианта использования библиотеки из 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. Любая критика приветствуется, я самоучка и многих моментов могу не знать.