如果我调用 wait 通知的条件变量会发生什么

What happens if i call wait on a notified condition variable

本文关键字:变量 什么 条件 调用 wait 通知 如果      更新时间:2023-10-16

假设我有两个线程和一个共享的 c++ 11 条件变量。如果线程 1 调用通知并在该线程 2 调用等待之后会发生什么?thread2 会永远阻塞还是由于线程 1 调用通知而继续工作?

编辑:

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() {};
    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)
            {
                return;
            }
            else if(lLoopCounter == lLoopCount)
            {
                bcLockGuard<bcMutex> lGuard(mMutex);
                mCond.wait(mMutex);                           
            }
            else
            {
                continue;
            }
        }
        void UnLock() 
        { 
            bcAtomicOperation::bcAtomicStore(mFlag, 0, bcMemoryOrder_Relaxed);
            bcUniqueLock<bcMutex> lGuard(mMutex);
            mCond.notifyOne();
        }
        bcBOOL TryLock() 
        {
        };
    };

我想编写一个自定义互斥锁,以便每个线程都可以提供一个参数,该参数表示当前线程要执行的操作的复杂性。如果操作的复杂性较低,则其他线程将像旋转锁一样处于循环中,但如果操作的复杂性中等,则每个线程将迭代 50 次,然后按条件变量休眠,如果操作非常复杂,其他线程将直接进入睡眠状态。

现在假设 thread1 锁定此互斥锁,

并且 thread2 由于其循环计数器到达其末尾而等待,并且在锁定条件变量的互斥锁之前,thread1 调用会通知条件变量。现在 thread2 将休眠,直到另一个线程锁定自定义互斥锁,然后对其调用 unlock 。

我是多线程的新手,我想学习。我知道我的类可能包含错误或可能完全错误,但是有没有办法纠正这个问题或编写这种互斥锁的好算法。

Thread2 将阻塞,直到有人调用通知。用于通知调用时正在等待的释放线程的调用。如果没有线程在等待,它们什么也不做。他们没有得救。

通常,

决定等待的代码和决定通知的代码共享相同的互斥锁。因此,线程 2 永远不会"错过"来自线程 1 的通知。

下面是经典的基于锁的并发队列示例:

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;
}

假设线程 1 和线程 2 分别运行push()pop()。一次只有一个将处于关键部分。

  • 如果 thread2 有锁,它要么从不等待,因为队列不为空(因此"丢失"通知是无害的(,要么它坐在那里等待通知(不会丢失(。

  • 如果 thread1 得到了锁,它会在队列中放置一个元素;如果 thread2 正在等待,
  • 它将得到正确的通知;如果 thread2 仍在等待互斥锁,它将永远不会等待,因为队列中至少有一个元素,因此丢失通知是无害的。

以这种方式,只有在一开始不需要通知时,通知才会丢失。

现在,如果您对条件变量有不同的用法,其中"丢失"通知会产生任何后果,我相信您要么有竞争条件,要么完全使用了错误的工具。

相关文章: