Что произойдет, если я вызову 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 все еще ждал мьютекса, он никогда не будет ждать, так как в очереди есть хотя бы один элемент, поэтому потеря уведомления безвредна.

таким образом, уведомление теряется, только если оно не было нужна в первую очередь.

Теперь, если у вас есть другое использование для переменных условий в виду, где "потеря" уведомления имеет какие-либо последствия, я считаю, что у вас либо есть состояние гонки, либо вы используете неправильный инструмент вообще.