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

时间:2013-10-02 16:49:53

标签: c++ multithreading c++11 condition-variable

假设我有两个线程和一个共享的c ++ 11条件变量。 如果thread1调用notify并且在thread2调用之后发生了什么? thread2会永久阻塞,还是会因为thread1的通知调用而继续工作?

修改:

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在条件变量上调用notify。现在thread2将一直处于休眠状态,直到另一个线程锁定自定义互斥锁,然后在其上调用unlock。

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

另一个问题:我的原子操作是否正确排序?

2 个答案:

答案 0 :(得分:16)

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

答案 1 :(得分:10)

通常,决定等待的代码和决定通知的代码共享相同的互斥锁。所以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仍在等待互斥锁,它将永远不会等待,因为队列中至少有一个元素,因此丢失通知是无害的。

通过这种方式,只有在首先不需要通知时才会丢失通知。

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