1

Linux の pthreads からの待機条件とミューテックスを使用してプロセス間の同期を行う際に奇妙な問題があります。これは、1 つのプロセス内のスレッド間だけではないことに注意してください。

私のユースケースは、リソース (私の場合は画像) を作成し、それらを共有メモリ領域に保存し、リソースに関する情報を更新してから、待機中のコンシューマーに通知するプロデューサーがあることです。共有メモリとメタデータの部分は正常に機能するので、省略します。問題は、シグナリングが確実に機能しないことです。ユースケースはシンプルで、消費者が画像を 1 つまたは 2 つ見落としても問題ありません。消費者がまだ画像を読み取る時間がない場合、プロデューサーは基本的に 1 つの古い画像を上書きするだけです。したがって、待機条件はコンシューマのウェイクアップを処理するだけでよく、リソース数やその他のデータは必要ありません。

プロデューサーとコンシューマーの両方に、次のような構造体があります。

struct EventData {
    pthread_mutex_t mutexHandle;
    pthread_cond_t  conditionHandle;
};

コンシューマ プロセスのスレッドは、何かが起こるのを待っています。

pthread_mutex_lock( &eventData->mutexHandle );
pthread_cond_wait( &eventData->conditionHandle, &eventData->mutexHandle );
thread_mutex_unlock( &eventData->mutexHandle );

プロデュース プロセスは、イメージを作成し、それを共有メモリに保存して、消費者がイメージを取得できるようになったときにこれを行います。

pthread_mutex_lock( &eventData->mutexHandle );
pthread_cond_signal( &eventData->conditionHandle );

// also tried:
//pthread_cond_broadcast( &eventData->conditionHandle );
pthread_mutex_unlock( &eventData->mutexHandle );

これは私にはかなり問題ないように見え、ある程度機能します。プロデューサーは問題なく消費者に約 100 ~ 1000 回信号を送ることができます。消費者は目を覚まし、画像をつかんで見せます。その結果、動画が動いているのを見ることができます。ある時点 (通常は数百フレーム前後) で、コンシューマーは pthread_cond_wait() でフリーズし、二度と戻りません。プロデューサは引き続き問題なくイメージを作成し、pthread_cond_signal() を呼び出して、問題なく続行します。コンシューマーは完全にフリーズしておらず、pthread_cond_wait() を実行するスレッドのみであり、残りのアプリケーションは問題なく実行されます。

そのため、あるスレッドから別のプロセスの別のスレッドに移動するときに、何らかの原因でシグナルが失われます。通常、消費者がフリーズするまでに 5 ~ 20 秒かかり、ウェイクアップの回数も 100 ~ 1000 の間で変動します (これまでに確認された値に基づく)。

デフォルトではミューテックスと待機条件をプロセス間で共有するのは簡単ではないため、このセットアップを使用してプリミティブを作成しました。

    EventData * eventData;

    int fd = open( tmpnam(NULL), O_RDWR | O_CREAT | O_EXCL, 0666);
    if (fd < 0) {
        // failed to open file for event
    }

    if ( ftruncate(fd, sizeof (eventData )) < 0 ) {
        // failed to truncate file
    }

    // setup attributes to allow sharing between processes
    pthread_condattr_init( &conditionAttribute );
    pthread_condattr_setpshared( &conditionAttribute, PTHREAD_PROCESS_SHARED );
    pthread_mutexattr_init( &mutexAttribute );
    pthread_mutexattr_setpshared( &mutexAttribute, PTHREAD_PROCESS_SHARED );

    // map memory for the event struct
    eventData = (EventData *) mmap(NULL, sizeof(EventData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close (fd);

    // finally initialize the memory
    pthread_mutex_init( &eventData->mutexHandle, &mutexAttribute );
    pthread_cond_init( &eventData->conditionHandle, &conditionAttribute );

上記は、ミューテックスと待機条件を作成する側によって行われます。ファイルの名前、つまり tmpnam(NULL) は実際には保存され、開くために他のプロセスに渡されます。

    int fd = open( nameOfEventFile, O_RDWR, 0666 );
    if (fd < 0) {
        // failed to open file for event
    }

    eventData = (EventData *) mmap( NULL, sizeof(EventData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
    close( fd );

ここにはエラーは見られません。特にランダムな時間で動作するため、何が問題になる可能性があるかについてのヒントが欲しいです。

4

1 に答える 1

1

そして、質問の95%を書き終えるとすぐに、エラーが目に飛び込んできました...他の誰かが同様のものに出くわした場合に備えて、修正とともにここに載せることにしました。ミューテックスと待機条件を作成する部分は次のようになります。

EventData * eventData;

int fd = open( tmpnam(NULL), O_RDWR | O_CREAT | O_EXCL, 0666);
if (fd < 0) {
    // failed to open file for event
}

if ( ftruncate(fd, sizeof (eventData )) < 0 ) {
    // failed to truncate file
}

注意深く見ると、ftruncate() が struct EventData のサイズではなく、eventData ポインターのサイズに切り捨てられることがわかります。したがって、ここで必要な 1 つの文字の修正は次のとおりです。

if ( ftruncate(fd, sizeof (EventData )) < 0 ) {
    // failed to truncate file
}

確かに愚かなバグ。

于 2012-11-14T11:14:24.947 に答える