2

以下のメカニズムを使用して、2 つ以上のスレッドを同期することを考えています。私が思う唯一の欠点は、CPU使用率です。このメカニズムに関するご意見をお寄せください。この実装に問題はありますか? (gcc の _sync *関数は移植可能であると仮定します)

//logic is that if lock = 1 means a thread has acquired the lock.
//                 lock = 0 means lock is free.
// acquireLock:
//        add 1 to lock if value is 1 this thread gets the lock
//        if value is > 1 mean some one else have taken the lock, so decrement the count


int lock = 0 ; // global variable.


void acquireLock()
{

    while(__sync_add_and_fetch (&lock,1) > 1)
    {
        __sync_add_and_fetch (&lock,-1);
    }

}

void releaseLock()
{
     __sync_add_and_fetch (&lock,-1);
}

したがって、共有データまたはグローバル データにアクセスするスレッドは、最初に acquireLock を呼び出し、グローバル データにアクセスしてから、Lock を解放します。

4

2 に答える 2

5

一種のスピンロックを実装しています。インクリメントとデクリメントの代わりに、代わりに CAS ループを使用できます。

void acquireLock () {
    while (!_sync_bool_compare_and_swap(&lock, 0, 1)) {}
}

void releaseLock () {
    int unlock_success = _sync_bool_compare_and_swap(&lock, 1, 0);
    assert(unlock_success);
}

比較とスワップは、変数の内容が期待値 (2 番目のパラメーター) と一致するかどうかを確認します。その場合、変数を新しい値 (3 番目のパラメーター) に設定します。期待値が一致しない場合、呼び出しは失敗します。これはアトミック操作です。

于 2013-06-28T01:29:57.453 に答える
1

@jxhが提案したのは良い解決策です。ただし、ソリューションの何が問題なのかについても尋ねたので、私の見解は次のとおりです。

「ロック取得の失敗」を 2 つのステップに分割しています。

  1. ロックを取得してみる (フラグをインクリメントする)
  2. 失敗した場合のバックアップ (フラグをデクリメント)

したがって、スレッドがロックの取得に失敗した場合でも、スレッドはグローバル状態を変更 (インクリメント) し、後で別の時点でこの変更を修正します。問題は、「修正」がすぐに、またはすぐに行われるという保証がないことです。インクリメントとデクリメントはそれ自体がアトミックですが、カップルとしてではありません。

基本的に、一部のスレッドがロックを取得しようとして失敗した場合、カウンターをすぐにデクリメントできない可能性があります (スレッドがスワップアウトされる可能性があります)。これで、現在のロック所有者がロックを解放しても、値が常に 1 より大きいため (つまり、「スリーパー」が戻ってデクリメントを行うまで)、他のスレッドはロックを取得できなくなります。これにより、ボトルネック (すべてのスレッドがすべての「失敗した」スレッドに依存してデクリメントを実行する) またはデッドロック (「失敗した」スレッドが何らかの理由でインクリメントして停止した場合) が作成されます。

@jxh のソリューションでは、ロックの取得 (失敗するか成功するかにかかわらず) は全体としてアトミックなステップであるため、これは発生しません。

于 2013-11-06T20:50:26.047 に答える