2017-04-30 13 views
1

たとえば、5つのスレッドを作成して印刷したいとします。 2番目のものの前に4番目のものを実行させるにはどうすればよいですか?ミューテックスでロックしようとしましたが、2番目のロックだけをロックする方法がわからないので、セグメンテーション・フォルトが発生します。スレッドをLinuxで別のスレッドのために待機させるには?

+1

コードを投稿してください。セマフォも見てください。 –

答えて

1

通常、操作の順序がのであり、これらの操作を実行するスレッドではありません。それは簡単な区別のように聞こえるかもしれませんが、実装を開始すると、大きな違いが見られます。必要なスレッドの数は考慮されていないため、より効率的な方法ですが、実行する操作やタスクの数、並行して実行できる操作の数や実行する必要がある可能性がある方法順序付けられているか順序付けられている。

しかし、学習の目的で、代わりにスレッドの順序を調べるのが理にかなっています。

OPは各ワーカースレッド関数の文字列へのポインタを渡します。それはうまくいくが、ちょっと変わっている。通常は、代わりに整数の識別子を渡す:

#include <stdlib.h> 
#include <inttypes.h> 
#include <pthread.h> 

#define ID_TO_POINTER(id) ((void *)((intptr_t)(id))) 
#define POINTER_TO_ID(ptr) ((intptr_t)(ptr)) 

IDタイプの変換 - Iは、典型的には、上記の符号付き整数であることが想定int又はlongいずれか - ポインタには、2人のキャストを介して行われます。最初のキャストはintptr_t型に定義され、<stdint.h>(これは<inttypes.h>をインクルードすると自動的にインクルードされます)はvoidポインタの値を保持できる符号付き整数型です。 2番目のキャストはvoidポインターになります。中間キャストは、あなたのIDが情報の潜在的な損失なしにvoidポインタから変換できない整数型の場合に警告を避けます(通常、 "異なるサイズ"の警告として記述されています)。

POSIX threadsを注文する最も簡単な方法は、それが操作やタスクやジョブを発注すること異ならないで、次に実行すべきスレッドのIDを保護するためのロックとして単一mutexを使用することで、関連condition variableのためそれらのIDが現れるまで待ちます。

残った1つの問題は、順序を定義する方法です。通常、ID値を増減するだけです。減少すると、スレッドはID値の降順で実行されますが、ID値-1(スレッド数が0以上であると仮定した場合)は常に「すべて完了」関係なく、使用するスレッド数の:

static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER; 
static pthread_cond_t worker_wait = PTHREAD_COND_INITIALIZER; 
static int    worker_id = /* number of threads - 1 */; 

void *worker(void *dataptr) 
{ 
    const int id = POINTER_TO_ID(dataptr); 

    pthread_mutex_lock(&worker_lock); 
    while (worker_id >= 0) { 
     if (worker_id == id) { 

      /* Do the work! */ 
      printf("Worker %d running.\n", id); 
      fflush(stdout); 

      /* Choose next worker */ 
      worker_id--; 
      pthread_cond_broadcast(&worker_wait); 
     } 

     /* Wait for someone else to broadcast on the condition. */ 
     pthread_cond_wait(&worker_wait, &worker_lock); 
    } 

    /* All done; worker_id became negative. 
     We still hold the mutex; release it. */ 
    pthread_mutex_unlock(&worker_lock); 

    return NULL; 
} 

注私はそのタスクが実行された直後に作業員出口を聞かせていませんでした。私は例を少し拡張したかったので、これはです:あなたは、アレイ内の演算の順序を定義したいとしましょう:

static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER; 
static pthread_cond_t worker_wait = PTHREAD_COND_INITIALIZER; 
static int    worker_order[] = { 0, 1, 2, 3, 4, 2, 3, 1, 4, -1 }; 
static int    *worker_idptr = worker_order; 

void *worker(void *dataptr) 
{ 
    const int id = POINTER_TO_ID(dataptr); 

    pthread_mutex_lock(&worker_lock); 
    while (*worker_idptr >= 0) { 
     if (*worker_idptr == id) { 

      /* Do the work! */ 
      printf("Worker %d running.\n", id); 
      fflush(stdout); 

      /* Choose next worker */ 
      worker_idptr++; 
      pthread_cond_broadcast(&worker_wait); 
     } 

     /* Wait for someone else to broadcast on the condition. */ 
     pthread_cond_wait(&worker_wait, &worker_lock); 
    } 

    /* All done; worker_id became negative. 
     We still hold the mutex; release it. */ 
    pthread_mutex_unlock(&worker_lock); 

    return NULL; 
} 

は少しどのように変化するかを参照してください?

第3のケースを考えてみましょう。別のスレッド、たとえばメインスレッドは、次に実行するスレッドを決定します。この場合、2つの条件変数が必要です.1つは作業者が待機するためのもので、もう1つはメインスレッドが待機するためのものです。

static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER; 
static pthread_cond_t worker_wait = PTHREAD_COND_INITIALIZER; 
static pthread_cond_t worker_done = PTHREAD_COND_INITIALIZER; 
static int    worker_id = 0; 

void *worker(void *dataptr) 
{ 
    const int id = POINTER_TO_ID(dataptr); 

    pthread_mutex_lock(&worker_lock); 
    while (worker_id >= 0) { 
     if (worker_id == id) { 

      /* Do the work! */ 
      printf("Worker %d running.\n", id); 
      fflush(stdout); 

      /* Notify we are done. Since there is only 
       one thread waiting on the _done condition, 
       we can use _signal instead of _broadcast. */ 
      pthread_cond_signal(&worker_done); 
     } 

     /* Wait for a change in the worker_id. */ 
     pthread_cond_wait(&worker_wait, &worker_lock); 
    } 

    /* All done; worker_id became negative. 
     We still hold the mutex; release it. */ 
    pthread_mutex_unlock(&worker_lock); 

    return NULL; 
} 

労働者は、ワーカースレッドが作成されると、その後、worker_done条件変数を待つworker_lockミューテックスを保持する必要があります最初に実行すべきかを決定するスレッド。最初のワーカーがそのタスクを完了すると、worker_cone条件変数に信号を送り、条件変数worker_waitを待ちます。その後、決定スレッドはworker_idを実行すべき次のIDに変更し、worker_wait条件変数でブロードキャストする必要があります。これは、決定スレッドがworker_idを負の値に設定するまで続きます。例えば:(pthread_cond_wait()経由でペアになっている場合)、ミューテックスと条件変数がどのように相互作用するかの方法:

int    threads; /* number of threads to create */ 
pthread_t  *ptids; /* already allocated for that many */  
pthread_attr_t attrs; 
int    i, result; 

/* Simple POSIX threads will work with 65536 bytes of stack 
    on all architectures -- actually, even half that. */ 
pthread_attr_init(&attrs); 
pthread_attr_setstacksize(&attrs, 65536); 

/* Hold the worker_lock. */ 
pthread_mutex_lock(&worker_lock); 

/* Create 'threads' threads. */ 
for (i = 0; i < threads; i++) { 
    result = pthread_create(&(ptids[i]), &attrs, worker, ID_TO_POINTER(i)); 
    if (result) { 
     fprintf(stderr, "Cannot create worker threads: %s.\n", strerror(result)); 
     exit(EXIT_FAILURE); 
    } 
} 

/* Thread attributes are no longer needed. */ 
pthread_attr_destroy(&attrs); 

while (1) { 

    /* 
     TODO: Set worker_id to a new value, or 
      break when done. 
    */ 

    /* Wake that worker */ 
    pthread_cond_broadcast(&worker_wait); 

    /* Wait for that worker to complete */ 
    pthread_cond_wait(&worker_done, &worker_lock); 
} 

/* Tell workers to exit */ 
worker_id = -1; 
pthread_cond_broadcast(&worker_wait); 

/* and reap the workers */ 
for (i = 0; i < threads; i++) 
    pthread_join(ptids[i], NULL); 

たくさんの練習なしで理解するのは難しいかもしれ上記の例の全てにおいて非常に重要な詳細は、あります。

スレッドがpthread_cond_wait()を呼び出すと、指定されたミューテックスをアトミックに解放し、条件変数で新しい信号/ブロードキャストを待機します。 「原子的」とは、2つの間に時間がないことを意味します。その間に何も起こりません。信号または放送が受信されたときにコールが戻ります。違いは、信号が1つのランダムウェイタに送られることです。ブロードキャストは条件変数--で待機しているすべてのスレッドに到達し、スレッドはロックを取得します。シグナル/ブロードキャストが最初にスレッドを起動させるかのように考えることができますが、pthread_cond_wait()はmutexを再度取得するときにのみ返されます。

この動作は、上記のすべての例で暗黙的に使用されています。特に、worker_lockミューテックスを保持している間は、pthread_cond_signal()/pthread_cond_broadcast()は常に実行されます。これにより、明示的に、または条件変数で待機している保持スレッドによって、worker_lockミューテックスのロックが解除された後にのみ、他のスレッドが起動して動作するようになります。

イベントやアクションの順序について有向グラフ(Graphvizを使用)を描画することが考えられましたが、この「回答」は既に長すぎます。私はあなた自身がそれをやることを提案します - おそらく紙の上に?私がこのすべてのことについて学んでいたとき、その種の視覚化はとても役に立ちました。

 
私は上記の計画について非常に不快であると感じますが、私は認めなければなりません。常に1つのスレッドしか実行されておらず、基本的にはです。:タスクが特定の順序で実行されるジョブは、1つのスレッドしか必要としません。

しかし、上記の例は、OPだけでなく、POSIXスレッドに興味のあるCプログラマーの方にも、mutexと条件変数の使い方をより快適にするために示しています。

関連する問題