0

私のスレッド学習テストのコードは次のとおりです。

int mylock = 0;

void *r1(void *x)
{
  puts("entered r1");
  int *p;
  p = (int *)x;
  int i = *p;
  sleep(1);
  *p = --i;
  printf("r1: %d\n",*p);
  mylock = 1;
  printf("r1: done\n");
#ifdef USETHREADS
  pthread_exit(0);
#endif
}

void *r2(void *x)
{
  puts("entered r2");
  if (!mylock) {
          puts("r2 is waiting...");
          while (!mylock)
                printf("");
  }
  int *p;
  p = (int *)x;
  int i = *p;
  *p = ++i;
  sleep(1);
  printf("r2: %d\n",*p);
  printf("r2: done\n");
#ifdef USETHREADS
  pthread_exit(0);
#endif
}

main()
{
  int i1,i2;
  i1 = 1;
  i2 = 2;
  printf("i1: %d\n", i1);
#ifdef USETHREADS
  pthread_t r1_thread, r2_thread;
  pthread_create(&r1_thread, NULL, r1, &i1);
  pthread_create(&r2_thread, NULL, r2, &i1);
  pthread_join(r1_thread, NULL);
  pthread_join(r2_thread, NULL);
#else
  r1(&i1);
  r2(&i1);
#endif
  printf("i1: %d\n", i1);
  return 0;
}

したがって、2 つのスレッド、1 つは i1 を増加させ、もう 1 つは減少させます (pthreads の「ミューテックス」は認識していますが、現時点では使用していません)。競合状態を回避するために、mylock を作成します (これはミューテックスを偽造していると思います)。 mylock が値を変更するのを待っている間に、プロセスが未定義ループに陥ってしまうのですか? 待機ループで呼び出さない限りprintf、printf 呼び出しで予想どおり 2 秒で終了します。それは Linux のミステリーですか?

4

2 に答える 2

4

プログラムは、mylock同期せずにアクセスすることにより、未定義の動作を呼び出しています。アクセスする前、および完了したら、 pthread_mutex_lock(の状態を保護するために選択したミューテックスで)を呼び出す必要があります。もちろん、thenはおそらく役に立たないでしょう。ロックに直接使用できます。mylockpthread_mutex_unlockmylockpthread_mutex_lock

そうは言っても、スレッドを学習しようとしている場合は、まったく間違った方法で取り組んでいます。独自の同期プリミティブをロールしようとすることから始めません。ほとんどの場合、専門家であっても、本当に特別な必要がない限り、これを行うべきではありません。同期プリミティブを正しく使用する方法を教えてくれる優れたスレッドのチュートリアルを入手してから、それらを使用してください

于 2013-09-15T19:19:40.940 に答える
0

コンパイラーはそれが変更されないと仮定することが許されているため、printfそれを使用してもスタックする可能性があります(実際には true であり、変更されませ)。printfmylockprintfmylock

この場合、次の形式のループが与えられます。

while (!x)
    some_code;

コンパイラは、ループ内を「見て回って」、 にsome_code与える影響 (存在する場合) を判断しxます。たとえば、ループが一度に 1 つずつカウントダウンすることがわかっている場合 (with などx -= 1)、コンパイラはxループの先頭で 1 回サンプリングしてから、ループを「展開」できます。x >= 4 の場合、これを次のように置き換えます。

while (x >= 4) {
    some_code;
    some_code;
    some_code;
    some_code;
}
switch (x) {
case 3:
    some_code;  /* and, no "break" */
case 2:
    some_code;
case 1:
    some_code;
}

さまざまなテストを排除することで実行時間を高速化します。( x「死んでいる」、つまり、ループの後、再び使用されない場合、3 つのクリーンアップのケースで、コンパイラはそのx -= 1部分も削除できます。)

ここで、x(というか、my_lock) はまったく変更されていないため、ループは無限です。コンパイラは、を次のように置き換えることができますwhile

if (my_lock)
    for (;;) continue;

この特定のケースでは、修飾子を使用して宣言することで、おそらく機能させることができます。my_lockvolatile

volatile int my_lock;

これは、変数で何か異常が発生したことをコンパイラーに伝えます。つまり、コンパイラーが通常の手段では認識できない方法で変更されています。ただし、これは保証されていませんが、pthread ミューテックスには保証が付いてます。このvolatileキーワードは、エンドユーザー向けというよりも、pthread ミューテックスの実装を作成する人が使用できるツールとしての意味があります。

于 2013-09-15T18:47:28.047 に答える