Я хочу написать пользовательский мьютекс, чтобы каждый поток мог предоставить аргумент, представляющий сложность операций, которые текущий поток хочет выполнить. Если сложность операции низкая, другие потоки будут находиться в цикле, таком как блокировка спина, но если сложность операции будет средней, каждый поток будет итерации 50 раз, а затем будет спящий по переменной условия, и если операция очень сложна, другие потоки будут отправляйтесь спать прямо.Способы включения потоков в спиновые блокировки
enum LockOperation
{
LockOperation_Light = -1,
LockOperation_Medium = 50,
LockOperation_Heavy = 1
};
class CustomMutex
{
private:
protected:
std::atomic<int> mFlag;
std::mutex mMutex;
std::condition_variable mCond;
public:
CustomMutex() { std::atomic_init(&mFlag, 0); };
~CustomMutex() {};
void lock(LockOperation pLockOperation = LockOperation_Medium)
{
int lNewLoopCount = static_cast<int>(pLockOperation);
int lLoopCounter = 0;
int lExpected = 0;
int lLoopCount = std::atomic_load_explicit(&mFlag, std::memory_order_relaxed);
while (true)
{
while(std::atomic_load_explicit(&mFlag, std::memory_order_relaxed) != 0 &&
lLoopCounter != lLoopCount)
++lLoopCounter;
std::atomic_compare_exchange_strong_explicit(
&mFlag,
&lExpected,
lNewLoopCount,
std::memory_order_acquire,
std::memory_order_relaxed);
if(lExpected == 0)
{
return;
}
else if(lLoopCounter == lLoopCount)
{
lExpected = 0;
std::unique_lock<std::mutex> lGuard(mMutex);
mCond.wait(lGuard);
}
else
{
lExpected = 0;
continue;
}
}
};
bcInline void UnLock()
{
std::atomic_store_explicit(&mFlag, 0, std::memory_order_relaxed);
std::lock_guard<std::mutex> lGuard(mMutex);
mCond.notify_one();
};
};
Предположит теперь thread1 замки этого мьютекс и thread2 идет для ожидания из-за его достижением его счётчик цикла конца и прямо перед блокированием мьютекса при условии переменного, thread1 вызовы оповещать о переменном состоянии. Теперь thread2 будет спать, пока другой поток не заблокирует этот мьютекс, а затем вызовет разблокировку.
Я новичок в многопоточности, и я хочу учиться. Я знаю, что мой класс может содержать ошибки или может быть совершенно неправильным, но есть ли способ исправить эту проблему или хороший алгоритм для написания такого мьютекса.
Другой вопрос: Правильно ли упорядочен порядок работы с атомами?
(извините, если мой вопрос как-то неоднозначное, мой английский не очень хорошо)
Спин-блокировки - плохая идея в пользовательском пространстве. В большинстве хорошо спроектированных приложений утверждение mutex на удивление редко встречается - и путь кода для «незащищенного» блокирования обычно оптимизирован. Классификация операций как «свет», «средний» или «тяжелый» является одной из тех вещей, которые заставляют меня думать о проблемах [приоритетной инверсии] (http://en.wikipedia.org/wiki/Priority_inversion). –
Я должен согласиться с @BrettHale :( –
@BrettHale Хорошо, но я думаю, что проблема приоритета инверсии в моем примере не встречается. Легкие, средние и тяжелые - это только флаги, владельцы мьютексов сообщают ожидающим потокам, сколько они должны зацикливаться до вызывая дорогостоящие системные вызовы.Я думаю, что нет никакого приоритетного спора между легкими и тяжелыми операциями, и получение мьютекса все еще происходит случайным образом. Почему вы говорите, что блокировка спина - это плохая идея? в «легких операциях», например, добавление или удаление элементов из связанного списка это будет хорошая идея, что мы позволяем потокам спина вместо блокировки вызова на мьютексе. (Я не понимаю, что «Замки спина - плохая идея в пользовательском пространстве») – MRB