2012-06-12 29 views
5

pthread条件変数のコードを理解するために、自分のバージョンを作成しました。それは正しいか?私はプログラムでそれを使用していますが、その作業は驚くほど速く動作します。もともとプログラムは約2.5秒かかっていて、私のバージョンの条件変数ではわずか0.8秒しかかからず、プログラムの出力も正しいです。しかし、私の実装が正しいかどうかはわかりません。条件変数の実装

struct cond_node_t 
{ 
    sem_t s; 
    cond_node_t * next; 
}; 

struct cond_t 
{ 
    cond_node_t * q;    // Linked List 
    pthread_mutex_t qm;     // Lock for the Linked List 
}; 

int my_pthread_cond_init(cond_t * cond) 
{ 
    cond->q = NULL; 
    pthread_mutex_init(&(cond->qm), NULL); 
} 

int my_pthread_cond_wait(cond_t* cond, pthread_mutex_t* mutex) 
{ 
    cond_node_t * self; 

    pthread_mutex_lock(&(cond->qm)); 
    self = (cond_node_t*)calloc(1, sizeof(cond_node_t)); 
    self->next = cond->q; 
    cond->q = self; 
    sem_init(&self->s, 0, 0); 
    pthread_mutex_unlock(&(cond->qm)); 

    pthread_mutex_unlock(mutex); 
    sem_wait(&self->s); 
    free(self); // Free the node 
    pthread_mutex_lock(mutex); 
} 

int my_pthread_cond_signal(cond_t * cond) 
{ 
    pthread_mutex_lock(&(cond->qm)); 
    if (cond->q != NULL) 
    { 
     sem_post(&(cond->q->s)); 
     cond->q = cond->q->next; 
    } 
    pthread_mutex_unlock(&(cond->qm)); 
} 

int my_pthread_cond_broadcast(cond_t * cond) 
{ 
    pthread_mutex_lock(&(cond->qm)); 
    while (cond->q != NULL) 
    { 
     sem_post(&(cond->q->s)); 
     cond->q = cond->q->next; 
    } 
    pthread_mutex_unlock(&(cond->qm)); 
} 
+0

'self'ノードをリストから削除せずに解放しています。 –

+0

@ n.m。 'self'ノードは' signal'と 'broadcast'によって取り除かれます。 –

+0

@JensGustedtはい、私の悪い –

答えて

1

は、基本的にはあなたの戦略は大丈夫に見えますが、あなたは一つの大きな危険、いくつかの未定義の動作、およびNITピック持って:あなたはあなたのPOSIX関数の戻り値を検査していない

  • を。特にsem_waitは中断されますので、重い負荷や不運のもとで、スレッドは偽りに目が覚めます。あなたは慎重にすべてをキャッチする必要があります
  • 値を返す関数はありません。ある関数のユーザーがいつか戻り値を使用することに決めた場合、これは未定義の動作です。条件関数が返すことが許されているエラーコードを慎重に分析し、それだけを行います。実は、あなたがすべてでmalloc/free必要はありません。
  • mallocまたはcalloc

編集のリターンをキャストしないでください。ローカル変数も同様です。

+0

malloc/callocの戻り値をキャストしないのはなぜですか? – pythonic

+1

最初に、最も重要なことは、役に立たないことです。 'void *'をポインタに代入することは有効です。これはCで作られたものです。次に、絶対に必要な場合にのみ、キャストを使用してください。彼らはテキストで見つけにくいし、すべての警告と診断をオフにする。第三に、これは微妙なバグを隠すことができます。あなたが '#include 'を忘れてコンパイラが' int'を返すのを忘れたときです。 –

2

あなたはこの要件を尊重していないようです:

これらの関数は、アトミックミューテックスを解放し、条件変数condをブロックする呼び出し元のスレッドを引き起こします。ここでは原子的には、「mutexへの別のスレッドによるアクセス、次に条件変数へのアクセスに関して原子的に」という意味の を意味します。つまり、about-to-blockスレッドが解放した後に別のスレッドがmutex を取得できた場合、そのスレッドのpthread_cond_broadcast()またはpthread_cond_signal()へのその後の呼び出しは、 は、 about-to-blockスレッドがブロックされています。

ロックを解除してから待機します。別のスレッドは、これらの操作の間に多くのことを行うことができます。

P.S.私はこの段落を正しく解釈しているのかどうかはわかりませんが、私の誤りを指摘してください。

+0

これは問題ではありません。セマフォは正しく初期化されています。したがって、別のスレッドが起動しても、セマフォは 'signal'または' broadcast'によって 'post'されるトークンを格納します。このような 'post'オペレーションは、スレッドが実際に' sem_wait'を呼び出す前に、または既に 'sem_wait'を呼び出している間に起こります。どちらの場合も、スレッドは引き続き実行を続けます。 –

4

は別に行方不明の戻り値のチェックから、修正可能である必要があり、いくつかのより多くの問題があります。

  • sem_destroyは呼び出されません。
  • シグナル/ブロードキャストは、ターゲットスレッドをウェイクした後にcond_node_tに触れます。

さらにコメント:

  • 省略は、POSIXが、それは安全でなければならないと言うとき、条件変数を破壊しても安全ですので、操作は他の操作への変更を必要とするかもしれない破壊します。破棄をサポートしていない場合や、呼び出される可能性がある場合にはより強い制限を課すことは、事柄を単純化します。
  • 実稼働の実装では、スレッドのキャンセルが処理されます。
  • (スレッドのキャンセルや、pthread_cond_timedwaitのタイムアウトに必要な)待機を取り消すと、問題が発生する可能性があります。
  • インプリメンテーションでは、パフォーマンス上の理由から、一部の本番実装で実行される、ユーザーランド内のスレッドをキューに入れます。なぜ私は正確に理解できません。
  • 実装は常にスレッドをLIFO順にキューに入れます。これはしばしば(キャッシュ効果などの理由により)より高速ですが、飢餓につながる可能性があります。生産の実装では、飢餓を避けるためにFIFO順序を使用することがあります。