web-dev-qa-db-ja.com

すでにロック解除されているミューテックスのロックを解除した場合、動作は未定義ですか?

すでにロック解除されているミューテックスのロックを解除した場合、動作は安全ではありませんか、安全ですか、それとも未定義ですか?

質問の目的は次のコードに関連しています。ifブロック内でミューテックスのロックを解除する方がよいのか、ifブロックのすぐ外側でロックを解除する方がよいのかわかりません。

    // This chunk of code makes dual locking semi-autonomous.
    int c_lckd = 0, q_lckd = 0;
    if (pthread_mutex_trylock(&crunch_mutex) == 0) c_lckd = 1;
    if (pthread_mutex_trylock(&queue_mutex) == 0) q_lckd = 1;
    if (q_lckd && !c_lckd) { QUEUE_UNLOCK; q_lckd = 0; }
    else if (c_lckd && !q_lckd) { CRUNCH_UNLOCK; c_lckd = 0; }

    if (c_lckd && q_lckd) {
      printf("cr = %d, max = %d, cnt = %d\n",
        crunching, max_crunching, queue_count(conn_queue));
      if (crunching < max_crunching && queue_count(conn_queue)) {
        pthread_t tid =
          pthread_create(
            &tid,
            NULL,
            crunch_conn,
            (void *)queue_dequeue(conn_queue)
          );
        crunching++;
      }
      CRUNCH_UNLOCK QUEUE_UNLOCK
    }

ありがとう、チェンツ

25
Crazy Chenz

Pthreadの場合、未定義の動作が発生します。 pthread_mutex_unlock のマニュアルページから:

呼び出し元のスレッドが保持していないミューテックスを使用してpthread_mutex_unlock()を呼び出すと、未定義の動作が発生します。

他のミューテックスには独自の動作があります。他の人が述べているように、使用しているミューテックスのマニュアルを読むのが最善です。

23
Glen

グレンが指摘したように、ロックされていないミューテックスを ロック解除 しようとすると、未定義の動作が発生します-試さないでください。スレッドのデバッグは、未定義の動作を呼び出さずに十分に困難です。

さらに重要なことに、コーディングスタイルは少し変わっています-両方のロックを取得しない限り何もしないので、それに応じてコーディングしてください:

if (pthread_mutex_trylock(&crunch_mutex) == 0)
{
    if (pthread_mutex_trylock(&queue_mutex) == 0)
    {
        printf("cr = %d, max = %d, cnt = %d\n",
               crunching, max_crunching, queue_count(conn_queue));
        if (crunching < max_crunching && queue_count(conn_queue))
        {
            pthread_t tid;
            int rc = pthread_create(&tid, NULL,
                               crunch_conn, (void *)queue_dequeue(conn_queue));
            if (rc != 0)
            {
                // Error recovery
                // Did you need what was returned by queue_dequeue()
                // to requeue it, perhaps?
            }
            else
            {
                crunching++;
                // Do something with tid here?
            }
        }
        QUEUE_UNLOCK;
    }
    CRUNCH_UNLOCK;
}

これにより、「やったか」という変数が回避されます。また、ロック解除マクロが期待どおりに動作する限り(そして、漂遊例外やsetjmpがない限り)、ロックされているミューテックスがロック解除されていることもすぐにわかります。また、クランチミューテックスが使用できない場合にキューミューテックスをロックするためのエネルギーの浪費を回避しますが、これは、追加された明快さに比べて小さな問題です。

9

一般に、このような質問の場合、ドキュメントが最良の情報源です。異なるミューテックスは異なる動作をする場合があります。または、単一のミューテックスに異なる動作を引き起こすオプションがある場合があります(単一のスレッドでミューテックスを再帰的に取得する場合など)。

2
Greg D

そのようにする必要はありません。これを試して:

    // This chunk of code makes dual locking semi-autonomous.
int c_lckd = 0, q_lckd = 0;
if (pthread_mutex_trylock(&crunch_mutex) == 0) c_lckd = 1;
if (pthread_mutex_trylock(&queue_mutex) == 0) q_lckd = 1;

if (c_lckd && q_lckd) {
  printf("cr = %d, max = %d, cnt = %d\n",
    crunching, max_crunching, queue_count(conn_queue));
  if (crunching < max_crunching && queue_count(conn_queue)) {
    pthread_t tid =
      pthread_create(
        &tid,
        NULL,
        crunch_conn,
        (void *)queue_dequeue(conn_queue)
      );
    crunching++;
  }

}

if (q_lckd) { QUEUE_UNLOCK; q_lckd = 0; }
if (c_lckd) { CRUNCH_UNLOCK; c_lckd = 0; }

従うのは少し簡単で、ロック解除されたミューテックスのロックを解除しようとするリスクはありません。

1
Michael Kohne

それを試してみてください。これは正しく機能するコードです。

// Mutex is not busy
if(pthread_mutex_trylock(&object->mtx) == 0) {
    if(pthread_mutex_unlock(&object->mtx)!=0) {
        perror("ERRN: pthread_mutex_unlock:");
    }
}
// Mutex is already busy
else {
    if(pthread_mutex_unlock(&object->mtx)!=0) {
        perror("ERRN: pthread_mutex_unlock:");
    }
}

// この時点で-ミューテックスのロックを正しく解除しました。

if(pthread_mutex_destroy(&object->mtx) != 0) {
    perror("ERRN: pthread_mutex_destroy:");
}
0
user6468372

ミューテックスのロック解除は、同じミューテックスが同じスレッドで以前にロックされている場合にのみ、スレッドで実行する必要があります。他のすべてのケースは、マニュアルページによると未定義の動作です。

ミューテックスタイプがPTHREAD_MUTEX_DEFAULTの場合、ミューテックスを再帰的にロックしようとすると、未定義の動作が発生します。呼び出し元のスレッドによってロックされていない場合にミューテックスのロックを解除しようとすると、未定義の動作が発生します。ロックされていない場合にミューテックスのロックを解除しようとすると、未定義の動作が発生します。

0
rashok