2017-08-04 6 views
0

私は整数をアトミックにインクリメントする方法を検討していましたが、ではなく、バインドチェックでとなりました。 私は他の投稿を見回しましたが、どれも良い解決策は見当たりません(そしていくつかはプレC++ 11です)。私は必要なものアトミックで彩度を調整する

は、次のようなライブラリです:のように使用する

class bounded_atomic_uint 
{ 
    private: 
    uint32_t ctr; 
    uint32_t max; 
    mutex mtx; 

    public: 
    bounded_atomic_uint(uint32_t max = UINT32_MAX) : ctr(0), max(max) {} 
    ~bounded_atomic_uint() = default; 
    // make in uncopyable and un-movable 
    bounded_atomic_uint(bounded_atomic_uint&&) = delete; 
    bounded_atomic_uint& operator=(bounded_atomic_uint&&) = delete; 

    bool inc(); 
    bool dec(); 
    uint32_t get(); 
}; 

bool bounded_atomic_uint::inc() { 
    lock_guard<mutex> lck (mtx); 
    if (ctr < max) { 
     ctr++; 
     return true; 
    } 
    else 
    { 
     cout << "max reached (" << max << ")" << endl; // to be removed! 
     return false; // reached max value 
    } 
} 

bool bounded_atomic_uint::dec() { 
    lock_guard<mutex> lck (mtx); 
    if (ctr > 0) { 
     ctr--; 
     return true; 
    } 
    else { 
     cout << "min reached (0)" << endl; // to be removed! 
     return false; // reached min value 
    } 
} 

uint32_t bounded_atomic_uint::get() { 
    lock_guard<mutex> lck (mtx); 
    return ctr; 
} 

#include <iostream> 
#include <mutex> 
#include <cstdint> 

using namespace std; 

int main() { 

    bounded_atomic_uint val(3); 

    if (val.dec()) 
     cout << "error: dec from 0 succeeded !!" << endl; 
    cout << val.get() << endl; // make sure it prints 0 
    val.inc(); 
    val.inc(); 
    cout << val.get() << endl; 
    if (!val.inc()) 
     cout << "error: havent reached max but failed!!" << endl; 

    if (val.inc()) 
     cout << "error max not detected!!" << endl; 

    cout << val.get() << endl; 

    return 0; 
} 

は、これを行うのいずれかの簡単な方法(またはより正確には)ありませんか? std :: atomicもboost :: atomicも、アウト・バウンド・チェック(ロック内)を回避する方法を持たないようです。

これがなければ、は簡潔クラスは十分ですか? ここに何もないのですか?ライブラリのcoutsが本当の使用に削除されることを

注意

+1

FYIあなたが探している用語は、「飽和演算」です。 –

答えて

2

あなたの質問is off-topicのこの部分ですので、その代わりに私がどのように実現できるかという質問に答えることができます。これはトピックであり、非常に興味深いものです。

はあなたの例からロックを削除し、atomicsと平野int Sを交換することにより開始するのをしてみましょう:

class bounded_atomic_uint 
{ 
    private: 
    atomic<uint32_t> ctr; 
    uint32_t max; 

    public: 
    bounded_atomic_uint(uint32_t max = UINT32_MAX) : ctr(0), max(max) {} 
    ~bounded_atomic_uint() = default; 
    // make in uncopyable and un-movable 
    bounded_atomic_uint(bounded_atomic_uint&&) = delete; 
    bounded_atomic_uint& operator=(bounded_atomic_uint&&) = delete; 

    bool inc(); 
    bool dec(); 
    uint32_t get(); 
}; 

bool bounded_atomic_uint::inc() { 
    if (ctr < max) { 
     ctr++; 
     return true; 
    } 
    else 
    { 
     cout << "max reached (" << max << ")" << endl; // to be removed! 
     return false; // reached max value 
    } 
} 

このコードの問題は、境界チェックと増分の間では、値はできる、ということです変更されました。したがって、あなたは競合がなければ範囲を壊さないということだけを保証することができます。

値を変更しないでインクリメントすることで、これを簡単に修正できます。境界の間のカウンタの変更はチェックして、増分の書き込み場合

bool bounded_atomic_uint::inc() { 
    while(true) { 
     auto ctr_old = ctr.load(); 
     if (ctr_old < max) { 
      if(ctr.compare_exchange_weak(ctr_old, ctr_old + 1)) { 
       return true; 
      } 
     } 
     else 
     { 
      cout << "max reached (" << max << ")" << endl; // to be removed! 
      return false; // reached max value 
     } 
    } 
} 

は今、compare_exchange_weakは失敗しますので、我々は再び試してみて下さい:これはcompare_exchangeが提供する正確に何です。その間に数値が範囲を超えた場合、次のループ反復でこれを検出し、それに応じて終了します。疑似エラー*をcompare_exchangeとして無視した場合、アトミックへの実際の同時書き込みがあった場合にのみループする必要があるので、この実装は実際にはlock-freeです。

我々はcompare_exchangeに、原子の繰り返し荷重を因数分解することで、これは少しより効率的に行うことができます(compare_exchangeが戻って最初の引数に、原子の実際の値を書き込むことに注意してください):

bool bounded_atomic_uint::inc() { 
    auto ctr_old = ctr.load(); 
    do { 
     if (ctr_old >= max) { 
      cout << "max reached (" << max << ")" << endl; // to be removed! 
      return false; // reached max value 
     } 
    } while(!ctr.compare_exchange_weak(ctr_old, ctr_old + 1)); 
    return true; 
} 

*我々代わりにcompare_exchange_strongを使用することによって偽の失敗を取り除くことができますが、とにかくループする必要があるため、実際にここで行う理由はありません。

+0

答えのThx。私の理解は、「アトミック」はロックフリーではないということです。実装が実際にロックフリーである場合、これは事実上はるかに良い解決策です(ただし、醜いですが:P)。しかし、原子が同期される場合、この解決策では少なくともロックを2回取得する必要があります1つは 'load()'、もう1つは 'compare_exchange()'です)、そうではありませんか? – Pacheco

+1

@Pacheco - 合理的なハードウェアアトミック操作ではロックフリーです。心配している場合は、 'atomic_is_lock_free'を使って確認してください。 –

+0

@Pacheco最初に、lock-freeプログラミングの* lock *部分は[リテラルロック(mutexのような)を参照していません。](http://preshing.com/20120612/an-introduction-to-ロックフリープログラミング/)。第二に、我々が原子に二度アクセスするという事実は、必要な最小限の時間しかアクセスできないため、システム全体のスループットを大幅に向上させる可能性があるという事実に反しています。逆の効果もあります。確実な唯一の方法は、両方を試して、目的のターゲットプラットフォームで測定することです。はい、マルチスレッドは難しいです... – ComicSansMS

関連する問題