13

假设我有两个线程和一个共享的 c++ 11 条件变量。如果thread1调用notify并且在thread2调用wait之后会发生什么?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() {};

    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 等待由于它的 loopCounter 到达它的结尾并且在锁定条件变量的互斥体之前,thread1 调用条件变量上的通知。现在 thread2 将休眠,直到另一个线程锁定自定义互斥体,然后对其调用 unlock。

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

4

2 回答 2

19

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

于 2013-10-02T17:05:14.757 回答
11

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

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

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

于 2013-10-02T18:39:53.360 回答