3

私はこの行で何かを実現したいと思います:

        inline void DecrementPendingWorkItems()
        {
            if(this->pendingWorkItems != 0) //make sure we don't underflow and get a very high number
            {
                ::InterlockedDecrement(&this->pendingWorkItems);
            }
        }

ロックを使用せずに、両方の操作がブロックとしてアトミックになるようにするにはどうすればよいですか?

4

5 に答える 5

2

の結果を確認し、InterlockedDecrement()たまたま負の場合 (または、より望ましい場合は <= 0) を呼び出してデクリメントを元に戻すことができますInterlockedIncrement()。それ以外の場合は適切なコードで問題ありません。

于 2012-07-11T08:54:23.533 に答える
2

最も簡単な解決策は、セクション全体 (および への他のすべてのアクセスthis->pendingWorkItems) でミューテックスを使用することです。何らかの理由でこれが受け入れられない場合は、おそらく比較と交換が必要になります。

void decrementPendingWorkItems()
{
    int count = std::atomic_load( &pendingWorkItems );
    while ( count != 0
            && ! std::atomic_compare_exchange_weak( 
                    &pendingWorkItems, &count, count - 1 ) ) {
    }
}

(これpendingWorkItemsは type を持つと仮定しますstd::atomic_int。)

于 2012-07-11T09:15:47.083 に答える
0

「SpinLock」というものがあります。これは非常に軽量な同期です。

これがアイデアです:

//
//    This lock should be used only when operation with protected resource
//  is very short like several comparisons or assignments.
//
class SpinLock
{
 public:

      __forceinline SpinLock() { body = 0; }
      __forceinline void Lock()
           {
             int spin = 15;
             for(;;) {
               if(!InterlockedExchange(&body, 1)) break;
               if(--spin == 0) { Sleep(10); spin = 29; }
             }
           }

      __forceinline void Unlock() { InterlockedExchange(&body, 0); }

 protected:

    long   body;

};

サンプルの実際の数値は重要ではありません。このロックは非常に効率的です。

于 2012-07-11T08:45:56.227 に答える
0

アトミック CAS を使用します。 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683560(v=vs.85).aspx

ロックフリーにすることはできますが、待機フリーにすることはできません。

キリルが示唆しているように、これはあなたの場合のスピンロックに似ています。

これは必要なことだと思いますが、まったくテストしていないため、先に進む前にすべての可能性を検討して使用することをお勧めします。

inline bool
InterlockedSetIfEqual(volatile LONG* dest, LONG exchange, LONG comperand)
{
    return comperand == ::InterlockedCompareExchange(dest, exchange, comperand);
}

inline bool InterlockedDecrementNotZero(volatile LONG* ptr)
{
    LONG comperand;
    LONG exchange;
    do {
        comperand = *ptr;
        exchange = comperand-1;
        if (comperand <= 0) {
            return false;
        }
    } while (!InterlockedSetIfEqual(ptr,exchange,comperand));
    return true;
}

保留中の作業項目がゼロ未満になる理由については、疑問が残ります。インクリメントの数がデクリメントの数と一致することを本当に確認する必要があり、すべて問題ありません。この制約に違反している場合は、おそらくアサートまたは例外を追加します。

于 2012-07-11T09:12:38.443 に答える
0

InterlockedCompareExchangeループで使用できます:

    inline void DecrementPendingWorkItems() {
        LONG old_items = this->pendingWorkingItems;
        LONG items;
        while ((items = old_items) > 0) {
            old_items = ::InterlockedCompareExchange(&this->pendingWorkItems,
                                                     items-1, items);
            if (old_items == items) break;
        }
    }

InterlockedCompareExchange関数が行っていることは次のとおりです。

  if pendingWorkItems matches items, then
    set the value to items-1 and return items
  else return pendingWorkItems

これはアトミックに行われ、比較およびスワップとも呼ばれます。

于 2012-07-11T08:53:52.957 に答える