13

2 つのスレッドと 1 つの共有 C++ 11 条件変数があるとします。thread1 が notify を呼び出し、その後 thread2 が wait を呼び出すとどうなりますか? スレッド 2 は永久にブロックされますか、それともスレッド 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 回反復し、条件変数によってスリープします。操作が非常に複雑な場合、他のスレッドは直接寝ます。

ここで、スレッド 1 がこのミューテックスをロックし、スレッド 2 が loopCounter が最後に到達したために待機し、条件変数のミューテックスをロックする直前にスレッド 1 が条件変数の通知を呼び出すとします。これで、スレッド 2 は、別のスレッドがカスタム ミューテックスをロックしてから unlock を呼び出すまでスリープします。

私はマルチスレッドが初めてで、学びたいと思っています。クラスにエラーが含まれているか、完全に間違っている可能性があることはわかっていますが、この問題を修正する方法や、そのようなミューテックスを作成するための優れたアルゴリズムはありますか?

4

2 に答える 2

19

スレッド 2 は、誰かが通知を呼び出すまでブロックします。呼び出し時に待機している解放スレッドを通知するための呼び出し。待機しているスレッドがない場合、それらは何もしません。それらは保存されません。

于 2013-10-02T17:05:14.757 に答える
11

通常、待機を決定するコードと通知を決定するコードの両方が同じミューテックスを共有します。したがって、スレッド 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()ます。そのうちの 1 つだけが一度にクリティカル セクションに入ります。

  • スレッド 2 にロックがある場合、キューが空ではないため待機しないか (つまり、通知を「失う」ことは無害です)、通知を待機して待機します (失われることはありません)。

  • スレッド 1 がロックを取得すると、要素がキューに入れられます。スレッド 2 が待機していた場合は、適切に通知されます。スレッド 2 がまだミューテックスを待っていた場合、キューに少なくとも 1 つの要素があるため、待機することはありません。そのため、通知が失われても問題はありません。

このように、通知が失われるのは、最初から必要がなかった場合のみです。

ここで、通知を「失う」と何らかの結果が生じる条件変数の別の使用法を念頭に置いている場合、競合状態にあるか、間違ったツールを完全に使用していると思います。

于 2013-10-02T18:39:53.360 に答える