今回は、スレッド間の相互排他制御についてやります。グローバル変数n(初期値0)があり、2つのスレッドがnが20未満まである限りnを1増やします。
スレッド関数は、次のようにします。
unsigend __stdcall mythread(void *lpx)
{
int esc = 0;
while (1) {
if (n < 20) {
printf("mythread n = %d\n", n);
n++;
} else {
esc = 1;
}
if (esc)
break;
}
return 0;
}
このスレッドは、nが20未満の条件を満たさなくなった時に終了します。
各スレッドはどのような順番で動作するかは全くわかりません。
しかし、nは共通なので表示は0,1,2,...19となるはずです。
しかし、実験してみると数字がダブったりします。 あるスレッドが共通の変数nにアクセスしている時、たまたま別のスレッドがnにアクセスすると不思議なことが起ります。もし、これがファイルの書き込み動作だとすると、このファイルは破壊されるかもしれません。
これを防ぐためには、あるスレッドがnにアクセスしている時、他のスレッドはnにアクセスできないような仕組みを作らなくてはいけません。
一番簡単なのがクリティカルセクションを作ることです。
1.CRITICAL_SECTION型変数を宣言する 2.InitializeCriticalSection関数でクリティカルセクションオブジェクトを初期化する 3.スレッドはEnterCriticalSection関数でクリティカルセクションオブジェクトの所有権を要求する (所有権を獲得するまで待機する) 4.スレッドは所定の処理を実行する 4.LeaveCriticalSection関数で所有権を放棄する 5.クリティカルセクションオブジェクトが不要になったらDeleteCriticalSection関数で解放するあるスレッドが、クリティカルセクションオブジェクトを所有していると、他のスレッドは所有することができません。
クリティカルセクションを利用すると、共有変数の同時アクセスを防ぐことができます。
各関数の引数はクリティカルセクションオブジェクトへのポインタです。 また、戻り値はすべてVOID型です。
では、サンプルのプログラムを見てみましょう。
/* mult05.c */
#include <stdio.h>
#include <windows.h>
#include <process.h>
unsigned __stdcall mythread0(void *);
unsigned __stdcall mythread1(void *);
int n;
CRITICAL_SECTION cs;
int main()
{
int i;
HANDLE hTh[2];
DWORD thID[2];
InitializeCriticalSection(&cs);
hTh[0] = (HANDLE)_beginthreadex(
NULL,
0,
mythread0,
NULL,
CREATE_SUSPENDED,
&thID[0]
);
if (hTh[0] == 0) {
printf("スレッド0作成失敗\n");
return -1;
}
hTh[1] = (HANDLE)_beginthreadex(
NULL,
0,
mythread1,
NULL,
CREATE_SUSPENDED,
&thID[1]
);
if (hTh[1] == 0) {
printf("スレッド1作成失敗\n");
return -1;
}
for (i = 0; i < 2; i++)
ResumeThread(hTh[i]);
WaitForMultipleObjects(2, hTh, TRUE, INFINITE);
for (i = 0; i < 2; i++)
CloseHandle(hTh[i]);
DeleteCriticalSection(&cs);
return 0;
}
unsigned __stdcall mythread0(void *lpx)
{
int esc = 0;
while (1) {
EnterCriticalSection(&cs);
if (n < 20) {
printf("mythread0 n = %d\n", n);
n++;
} else {
esc = 1;
}
LeaveCriticalSection(&cs);
if (esc)
break;
}
return 0;
}
unsigned __stdcall mythread1(void *lpx)
{
int esc = 0;
while (1) {
EnterCriticalSection(&cs);
if (n < 20) {
printf("mythread1 n = %d\n", n);
n++;
} else {
esc = 1;
}
LeaveCriticalSection(&cs);
if (esc)
break;
}
return 0;
}
実行結果は、次のようになります。
スレッド0とスレッド1が交互に、nを表示しているのがわかります。nもきちんと0から19まで
表示されているのがわかります。
Update Jun/06/2004 By Y.Kumei