5

データが利用可能になったときにプロデューサーがフラグを設定する必要がある、1 プロデューサー 1 コンシューマー モデルがあります。次の理由により、共有フラグをロックせずに逃げることができると思います。

  • プロデューサーは設定前に値をチェックしません
  • フラグの更新が時折見逃されることは問題ではありません (ただし、アトミック操作を使用してこれを回避することもできますか?)。

だから私の質問は、これをどのように実装するのですか? volatile キーワードや __sync_synchronize() などについての私の理解はせいぜい不十分なので、ほとんど知らないと仮定します。具体的には、フラグの変更がタイムリーに他のスレッドに表示されるようにしたいと考えています。

編集:LinuxでGCCを使用しています。

4

4 に答える 4

1

2 つの変数を使用します。

volatile size_t elements_produced; // producer increments it when data is available
volatile size_t elements_consumed; // consumer increments it

新しいデータは正確にelements_produced != elements_consumed. 無限の量が必要な場合は、さらに更新時に変更します。

produce(...) {
    elements_produced = (elements_produced + 1) % (max_elements_in_queue + 1);
}

consume(...) {
    elements_consumed = (elements_consumed + 1) % (max_elements_in_queue + 1);
}

ロックやアトミックは必要ありません。

これは、シングル プロデューサー、シングル コンシューマーの循環リング バッファーの標準的な実装です。

于 2012-10-08T20:10:07.787 に答える
0

移植可能な方法でこれを行うことは実際には不可能です。

ただし、さまざまなコンパイラ組み込み関数を使用して実装できます。

例として、x86(-64) およびおそらく少なくとも ARM 上の gcc の場合:

static int queued_work;

static void inc_queued_work()
{
    (void)__sync_add_and_fetch( &queued_work, 1 );
}

/*
  Decrement queued_work if > 0.
  Returns 1 if queued_work was non-equal to 0 before
  this function was called.
*/
static int dec_queued_work()
{
    /* Read current value and subtract 1.
       If the result is equal to -1, add 1 back and return 0.
    */
    if( __sync_sub_and_fetch( &queued_work, 1 ) == -1 )
    {
        __sync_fetch_and_add( &queued_work, 1 );
        return 0;
    }
    return 1;
}

一部の CPU: は、compare_and_swap のみをサポートします。その組み込み関数を使用してこれを実装することもできます。

/* Alternative solution using compare_and_swap  */
void inc_queued_work()
{
    do {
        int queued = queued_work;
        /* Try to write queued-1 to the variable. */
        if( __sync_bool_compare_and_swap( &queued_work,
                                         queued, queued+1 ) )
            return;
    } while( 1 );
}

int dec_queued_work()
{
    do {
        int queued = queued_work;
        if( !queued ) return 0;
        /* Try to write queued-1 to the variable. */
        if( __sync_bool_compare_and_swap( &queued_work, 
                                         queued, queued-1 ) )
            return queued;
    } while( 1 );
}

これらの関数は、ライターとリーダーが複数ある場合でも機能するはずです。友達と「sync_add_and_fetch」にグーグルを適用すると、多くの参考資料が得られます

于 2012-10-12T19:45:57.460 に答える
0

アトミック操作とは、最初の操作が完了するまで、他のアトミック操作を (非常に簡単に) ブロックすることを意味します。

スレッドについて話していると思いますので、ミューテックスについて考えるべきです (ただし、これはプロセスとセマフォにも当てはまります)。ミューテックス (またはセマフォ) は、ロックを取得することなくチェック (読み取り) できます。

ミューテックス (セマフォ) の状態が既にロックされている場合は、他の操作を続行し、後で再試行してください。

于 2012-10-08T20:09:30.187 に答える
-1

ロックがなければ、2 つのスレッドは決して同期せず、コンシューマは決して変更されない値を待機して永遠にスピンします (その値がキャッシュされ、メモリのプット/フェッチが発生しないため)。

要約すると、a) メモリーがプロデューサーから書き込まれたことを確認し、b) メモリーがコンシューマーによって読み取られたことを確認する必要があります。これはまさにロックの機能です。そのため、ロックを使用する必要があります。ロックに完全に反対している場合は、目覚めた後のキャッシュの状態を保証するスリープなどの機能を使用できます。

于 2012-10-08T21:10:16.247 に答える