2012-01-23 20 views
3

私の4つのCPUコアで同時に4つのスレッドを作成して機能を実行しようとしています。関数iは、val変数の値に応じていくつかのループオフセットを変更します。CreateThreadパラメータの値が予期せず変更される

私はこれを試してみましたが、それは正しくvalカウンタを増やすdoesntの、スレッドのいくつかは同じ値を報告し、ランダムに変化するようだ:

int val = 1; 
threads[0] = CreateThread(0, 0, my_thread_1, &val, 0, 0); 
val++; 
threads[1] = CreateThread(0, 0, my_thread_1, &val, 0, 0); 
val++; 
threads[2] = CreateThread(0, 0, my_thread_1, &val, 0, 0); 
val++; 
threads[3] = CreateThread(0, 0, my_thread_1, &val, 0, 0); 

しかし、これだけで正常に動作するようです:

int val1 = 1; 
int val2 = 2; 
int val3 = 3; 
int val4 = 4; 
threads[0] = CreateThread(0, 0, my_thread_1, &val1, 0, 0); 
threads[1] = CreateThread(0, 0, my_thread_1, &val2, 0, 0); 
threads[2] = CreateThread(0, 0, my_thread_1, &val3, 0, 0); 
threads[3] = CreateThread(0, 0, my_thread_1, &val4, 0, 0); 

これにはどのような原因があり、スレッドにパラメータを与えるために適切な方法はありますか?

これは私の関数である:

DWORD WINAPI my_thread_1(void *params){ 
    int val = *(int *)params; 
... 
} 

答えて

4

これは、最初の例では、すべての4つのスレッドについて同じメモリ位置にへのポインタを渡しているため、失敗します。 の値をに増やしてポインタを渡す前に、メモリの位置は変わらないので、重要ではありません。

2番目の例では、代わりに4つのスレッドにという相互排他的ポインタを渡します。したがって、スレッドはすべて独立した値を読み取ります。

あなたは保守性と柔軟性を支援するために、わずかにあなたのコードを再構築できます。最初のケースで

int threadData[NTHREADS]; /* in the future this could be an array of structs */ 
HANDLE threads[NTHREADS]; 
int tt; 

for (tt = 0; tt < NTHREADS; ++tt) 
{ 
    threadData[tt] = tt + 1; /* placeholder for actual logic */ 
    threads[tt] = CreateThread(
     NULL,   /* lpThreadAttributes */ 
     0,    /* dwStackSize */ 
     my_thread_1,  /* lpStartAddress */ 
     &threadData[tt], /* lpParameter: each thread will receive its own data */ 
     0,    /* dwCreationFlags */ 
     NULL    /* lpThreadId */); 
} 

/* Since threadData and threads are local to the enclosing scope, 
* we must wait for them to finish here to ensure we don't read 
* data we no longer own 
*/ 
WaitForMultipleObjects(NTHREADS, threads, TRUE, INFINITE); 
0

複数のスレッドが同時に同じメモリにアクセスしています。クリティカルセクションの変数への排他的アクセスには、ミューテックスまたはセマフォを使用する必要があります。

+0

それにもかかわらず、に保証第2の例であります正しく機能します? – Rookie

+1

@Rookieがコードになりました、はい。 –

+2

ロックはここでは絶対に答えではありません。答えはデータを共有することではありません。これは常にロックに適しており、意味的には正しいことです。 –

1

を、あなたはすべてのスレッドに同じオブジェクトを渡しています。後者の場合は、別のオブジェクトを渡しています。

どちらの場合も、スレッドのリターンを作成する関数場合は、すべての作成したスレッドは、すべてのintのオブジェクトがどの関数にローカルオブジェクトであるので、もはや存在しないintへのポインタを持っていることを潜在的な問題がありますスレッドを作成しました。これにより、未定義の動作が呼び出されます。

機能が待って戻っていないのであれば、その場合には、あなたの代わりに動的に割り当てられたオブジェクトを渡し、ローカルオブジェクトへのポインタを渡してはいけません。

int *v1 = new int; 
threads[0] = CreateThread(0, 0, my_thread_1, v1 , 0, 0); 
+0

私のスレッドが停止する前に、それらのスレッドを作成する私の関数が死んでいなければ、2番目の例が正しく動作することが保証されます。 – Rookie

+1

@Rookie:はい。スレッドを作成する関数が、スレッドがジョブを終了するまで戻らない場合、それは明確に定義されています。 – Nawaz

+3

確かに、またはすべてのスレッドが渡された値を使い果たし、それを必要としなくなったり、ローカルコピーを作成したりするまで待ってください。 –

関連する問題