5

マルチスレッドの Linux アプリケーションでは、クリティカル セクションにミューテックスを使用しています。これは、公平性の問題を除けば非常にうまく機能します。スレッドがクリティカル セクションを離れてすぐに再入力しても、他のスレッドにチャンスが与えられない場合があります。例えば

while(true)
{
    critsect.enter();
    ... do calculations ...
    ... maybe call a blocking operation so we sleep ...
    critsect.leave();
}

他のスレッドが同じクリティカル セクションに入るのを止める可能性が非常に高いです。Mutexe は公平ではありません。

公正なクリティカル セクションを作成するための解決策はありますか? クリティカルセクションが「到着」順に実行されるように、キューを追加することを考えていました。または、少なくとも、他のスレッドが待機している場合にロック解除後に pthread_yield() を実行するためのカウンター。

この種の要件に対して推奨される方法はありますか?

4

5 に答える 5

8

次の行に沿って、pthreadsミューテックスの上にFIFO「チケットロック」を構築できます。

#include <pthread.h>

typedef struct ticket_lock {
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    unsigned long queue_head, queue_tail;
} ticket_lock_t;

#define TICKET_LOCK_INITIALIZER { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }

void ticket_lock(ticket_lock_t *ticket)
{
    unsigned long queue_me;

    pthread_mutex_lock(&ticket->mutex);
    queue_me = ticket->queue_tail++;
    while (queue_me != ticket->queue_head)
    {
        pthread_cond_wait(&ticket->cond, &ticket->mutex);
    }
    pthread_mutex_unlock(&ticket->mutex);
}

void ticket_unlock(ticket_lock_t *ticket)
{
    pthread_mutex_lock(&ticket->mutex);
    ticket->queue_head++;
    pthread_cond_broadcast(&ticket->cond);
    pthread_mutex_unlock(&ticket->mutex);
}

この種のスキームでは、スレッドがチケットロックで保護されたクリティカル セクション内にある間、低レベルの pthreads ミューテックスは保持されず、他のスレッドがキューに参加できるようになります。

于 2011-06-23T12:24:28.720 に答える
4

かなりのクリティカル セクションがあっても、クリティカル セクションが長時間保持されているとスレッドがそれを待機することが多いため、コードのパフォーマンスはおそらく最悪になります。

したがって、重要なセクションを長期間ロックする必要がないように、コードを再構築することをお勧めします。完全に異なるアプローチを使用するか (メッセージ キューを介してオブジェクトを渡すことが推奨されることがよくあります。これは、正しく行うのが簡単なためです)、少なくともロックを保持せずにローカル変数でほとんどの計算を行い、ロックを取得して結果を格納するだけではありません。ロックが保持される時間が短いと、スレッドがそれを待機する時間が短くなり、一般にパフォーマンスが向上し、公平性が問題になりません。ロックの粒度を上げて (小さいオブジェクトを個別にロックする)、競合を減らすこともできます。

編集:わかりました、考えてみると、Linuxのすべての重要なセクションはほぼ公平だと思います。スリーパーが存在するときはいつでも、ロック解除操作はカーネルに入り、それらをウェイクアップするように指示する必要があります。カーネルからの復帰時に、スケジューラが実行され、優先度が最も高いプロセスが選択されます。待機中にスリーパーの優先度が上がるため、ある時点で、解放によってタスクの切り替えが発生するほど高くなります。

于 2011-06-23T08:05:43.193 に答える
0

あなたの主張が正しい場合(私は読む時間がなく、質問を投稿する前にこれを調査したように見えます)、私は提案します

 sleep(0);

クリティカル セクション間で明示的に譲歩します。

while(true)
{
    critsect.enter();
    ... do calculations ...
    ... maybe call a blocking operation so we sleep ...
    critsect.leave();
    sleep(0);
}
于 2011-06-23T06:41:30.330 に答える
0

OK、これはどうですか:

while(true)
{
    sema.wait;
    critsect.enter();
    sema.post;
    ... do calculations ...
    ... maybe call a blocking operation so we sleep ...
    critsect.leave();
}

初期化。セマフォのカウントを 1 にします。CS を取得しようとする前に、他のスレッドもセマフォで待機させ、完了したらそれを通知します。「計算」スレッドがセマを取得すると、CS に到達してロックできます。ロックに入ると、長い claculate の前に、sema が通知され、別の 1 つのスレッドが CS に到達できますが、その中に入ることはできません。「計算」スレッドがロックを終了すると、sema が原因でループして再ロックすることはできません。count が 0 であるため、他のスレッドがロックを取得します。「計算」スレッドは、入ってきた他のスレッドがそのアクセスを終了し、sema を通知するまで、sema で待機する必要があります。

このようにして、別のスレッドがデータへのアクセスを実際にはまだ取得できない場合でも、そのデータへのアクセスを「予約」できます。

Rgds、マーティン

于 2011-06-24T13:19:48.893 に答える