Что произойдет, если я вызову wait на уведомленной переменной условия
предположим, что у меня есть два потока и одна общая переменная условия c++ 11. что произойдет, если вызов thread1 уведомит и после этого вызова thread2 ждать? будет ли thread2 блокировать навсегда или он продолжит свою работу из-за вызова notify по thread1?
Edit:
enum bcLockOperation
{
bcLockOperation_Light = -1,
bcLockOperation_Medium = 50,
bcLockOperation_Heavy = 1
}
class BC_COREDLL_EXP bcCustomMutex
{
private:
bcCustomMutex(const bcCustomMutex&);
bcCustomMutex& operator=(const bcCustomMutex&);
protected:
bcAtomic<int> mFlag;
bcMutex mMutex;
bcConditionVariable mCond;
public:
bcCustomMutex() { bcAtomicOperation::bcAtomicInit(mFlag, 0); };
~bcCustomMutex() {};
/*bcMutex(const bcMutex& pOther) = delete;
bcMutex& operator=(const bcMutex& pOther) = delete;*/
bcInline void lock(bcLockOperation pLockOperation = bcLockOperation_Medium)
{
bcINT32 lNewLoopCount = static_cast<bcINT32>(pLockOperation);
bcINT32 lLoopCounter = 0;
bcINT32 lExpected = 0;
bcINT32 lLoopCount = bcAtomicOperation::bcAtomicLoad(mFlag, bcMemoryOrder_Relaxed);
while (true)
{
while(bcAtomicOperation::bcAtomicLoad(mFlag, bcMemoryOrder_Relaxed) != 0 &&
lLoopCounter != lLoopCount)
++lLoopCounter;
bcAtomicOperation::bcAtomicCompareExchangeStrong(
mFlag,
&lExpected,
lNewLoopCount,
bcMemoryOrder_Acquire,
bcMemoryOrder_Relaxed);
if(lExpected == 0)
{
//mMutex.lock();
return;
}
else if(lLoopCounter == lLoopCount)
{
bcLockGuard<bcMutex> lGuard(mMutex);
mCond.wait(mMutex);
}
else
continue;
}
};
bcInline void UnLock()
{
bcAtomicOperation::bcAtomicStore(mFlag, 0, bcMemoryOrder_Relaxed);
bcUniqueLock<bcMutex> lGuard(mMutex);
mCond.notifyOne();
};
bcInline bcBOOL TryLock()
{
};
};
Я хочу написать пользовательский мьютекс, чтобы каждый поток мог предоставить аргумент, представляющий сложность операций, которые текущий поток хочет выполнить. Если сложность операция низкая другие потоки будут в цикле, как спин-замок, но если сложность операции средняя, каждый поток будет повторяться 50 раз, а затем будет спать по переменной условия, и если операция очень сложная, другие потоки будут спать непосредственно.
теперь предположим, что thread1 блокирует этот мьютекс, а thread2 ждет из-за того, что его loopCounter достигает своего конца и прямо перед блокировкой мьютекса переменной условия, вызовы thread1 уведомляют переменную условия. Сейчас thread2 будет спать, пока другой поток не заблокирует пользовательский мьютекс, а затем вызовет разблокировку на нем.
Я новичок в многопоточности, и я хочу учиться. Я знаю, что мой класс может содержать ошибки или может быть полностью неправильным, но есть ли способ исправить эту проблему или хороший алгоритм для написания такого мьютекса.
другой вопрос: Правильно ли упорядочена моя атомная операция?
2 ответов
Thread2 будет блокировать, пока кто-то не вызовет уведомление. Вызовы для уведомления потоков выпуска, ожидающих во время вызова. Если нити не ждут, они ничего не делают. Они не спасены.
обычно и код, который решает подождать, и код, который решает уведомить, используют один и тот же мьютекс. Таким образом, thread2 никогда не" пропустит " уведомление от thread1.
вот классический пример параллельной очереди на основе блокировки:
void push(int x)
{
lock_guard<mutex> guard{queue_mutex};
thequeue.push(x);
not_empty_condition.notify_one();
}
int pop()
{
unique_lock<mutex> guard{queue_mutex};
not_empty_condition.wait(guard, []{ return !thequeue.empty(); } );
int x = thequeue.front();
thequeue.pop();
return x;
}
предположим, что thread1 и thread2 работают push()
и pop()
соответственно. Только один из них будет находиться в критической секции одновременно.
Если thread2 имеет замок, либо он никогда не ждет, потому что очередь не пуста (поэтому "потеря" уведомления безвредна), или она сидит там, ожидая уведомления (которое не будет потеряно).
Если thread1 получил блокировку, он поместит элемент в очередь; если thread2 ждал, он получит уведомление должным образом; если thread2 все еще ждал мьютекса, он никогда не будет ждать, так как в очереди есть хотя бы один элемент, поэтому потеря уведомления безвредна.
таким образом, уведомление теряется, только если оно не было нужна в первую очередь.
Теперь, если у вас есть другое использование для переменных условий в виду, где "потеря" уведомления имеет какие-либо последствия, я считаю, что у вас либо есть состояние гонки, либо вы используете неправильный инструмент вообще.