1
gcc 4.7.2
c89

こんにちは、

次のコードスニペットにミューテックスロックを適用することについて疑問に思っています。

この関数の他のスレッドをブロックするのでロックしたくないので、従う必要のあるルールはありますか?これは本当に物事を遅くするので。

私は次のCFLAGSでコンパイルしています:

-Wall -Wextra -g -m32 -O2 -D_DEBUG -D_THREAD_SAFE -D_REENTRANT -D_LARGEFILE64_SOURCE

コードスニペット

static void* APR_THREAD_FUNC timeout_duration(apr_thread_t *thd, void *data)
{
apr_status_t rv = 0;
channel_t *channel = NULL;

/*
  APPLY LOCK HERE
*/
channel = (channel_t*)data;

/* simulate some work */
apr_sleep(5000000);

LOG_INFO("Channel id [ %d ] Channel name [ %s ] Delay time [ %d ]",
         channel->id,
         channel->name,
         (apr_int32_t)channel->delay_time);

/*
  UNLOCK HERE
*/

return NULL;
}

エントリ関数に渡されるデータとしてチャネルを渡します。しかし、これは単なるコピーではないので、私はそれについて本当に心配する必要はありませんか?

4

4 に答える 4

3

ルールは次のとおりです。

1) ロックはコードではなくデータを保護します。データがロックによって保護されている場合、そのデータにアクセスするコードはデータのロックを取得する必要があります。

2) ロックはできるだけ遅く取得し、できるだけ早く解放する必要があります。これには、クリティカル セクション内からクリティカル セクション外への作業のシフトが含まれる場合があります。

3) 読み取り専用 (および変更されていない) のデータは、ロックを必要としません。これには、"Channel id [ %d ] ..."フォーマット文字列 (定数として扱う必要があります) などが含まれます。

4) 1 つのスレッドのみがアクセスできるデータは、ロックを必要としません。これには、関数パラメーターやローカル変数などが含まれます。

5) 粒度の細かいロックは、粒度の粗いロックよりも優れています。たとえば、1 つのロックを持つ 1 つの大きなデータ構造を持つのではなく、多くの場合、その大きなデータ構造を多くのロックを持つ多くの小さな構造に分割できます。

6) 一度に複数のロックが必要なコードがある場合は、「ロック順序」を定義する必要があります。たとえば、あるスレッドがロック A を取得してからロック B を取得した場合、何らかの作業を行ってからロックを解放します。別のスレッドがロック B を取得してからロック A を取得した場合、何らかの作業を行ってからロックを解放します。その後、デッドロックが発生する可能性があります (各スレッドには 1 つのロックがありますが、続行するには両方が必要です)。「ロック順序」を定義すると (たとえば、ロック A はロック B の前に取得する必要があると言う)、この種のバグを防ぐことができます。

コードでは、最初の数行は関数パラメーターとローカル変数にのみアクセスするため、ロックはまったく必要ありません (ルール 4)。が指すデータはvoid *data、それが何であるかに応じて、ロックを必要とする場合と必要としない場合があります。たとえば、各スレッドが独自の個別のデータを持っている場合 (ルール 4)、またはそのデータが読み取り専用の場合 (ルール 3)、ロックは必要ありません。関数については、投稿したコードにLOG_INFO()追加のロックは必要ありません (存在する場合はロックを除くvoid *data) が、独自の内部ロックがある場合があります (共有ログを保護するためなど)。

ルール 2 の例としてLOCK_INFO、少し時間がかかる場合、コードで次のようなことを実行して、最初のロックを早期に解放できます。

temp_ID = channel->id;
temp_name = strdup(channel->name);  // Should check for NULL!
temp_delay = channel->delay_time;

/*
  UNLOCK HERE
*/

LOG_INFO("Channel id [ %d ] Channel name [ %s ] Delay time [ %d ]",
         temp_ID,
         temp_name,
         temp_delay);
free(temp_name);

LOCK_INFO()また、ロックを使用する場合は、最初のロックを早期に解放するとルール 6 にも役立つことに注意してください。

于 2013-02-07T14:40:01.783 に答える
2

ミューテックスがロック解除されているときにこのチャネルを読み取りたい場合は、pthread_mutex_lock()の代わりにpthread_mutex_trylock()を使用してください。それは同じことをしますが、pthread_mutex_trylock()は、あなたができる戻り値に基づいて、ブロック的ではありません:

if (pthread_mutex_trylock(your_mutex))
{
    //Read the data.
}
于 2013-02-07T14:28:07.717 に答える
1

実際のデータdataへのポインタと同様に、あなたは間違いなくそれについて心配する必要があります。channel_t

ただし、スレッドを使用して並行して作業を行う場合は、ロックを保持したまま作業期間を非常に長くすると意味がありません。きめ細かいロックの方が良いでしょうが、もちろん、実行される操作全体をセクションに分割して、データを一貫した状態に保つことができると想定しています。

于 2013-02-07T14:25:06.977 に答える
1

いいえ、それはコピーではなく、実際にはデータへのdata単なるポインターです。その結果、スレッドと呼び出し元コンテキストの両方がこの同じデータにアクセスできます。ここで、両方 (スレッドと呼び出し元のコンテキスト) が同時にデータに触れないことを保証できる場合、スレッド関数でロックする必要はありません。

于 2013-02-07T14:22:34.227 に答える