スレッドの作成
CreateThread 関数は、プロセスの新しいスレッドを作成します。 作成スレッドは、新しいスレッドが実行するコードの開始アドレスを指定する必要があります。 通常、開始アドレスはプログラム コードで定義されている関数の名前です (詳細については、「 ThreadProc」を参照してください)。 この関数は 1 つのパラメーターを受け取り、 DWORD 値を返します。 プロセスは、同じ関数を同時に実行する複数のスレッドを持つことができます。
ローカルに定義された 関数 MyThreadFunction
を実行する新しいスレッドを作成する方法を示す簡単な例を次に示します。
呼び出し元のスレッドは WaitForMultipleObjects 関数を使用して、すべてのワーカー スレッドが終了するまで保持します。 呼び出し元のスレッドは、待機中にブロックします。処理を続行するには、呼び出し元のスレッドは WaitForSingleObject を使用し、各ワーカー スレッドがその待機オブジェクトを通知するまで待機します。 ワーカー スレッドが終了する前にハンドルを閉じると、ワーカー スレッドは終了しないことに注意してください。 ただし、後続の関数呼び出しでは、ハンドルを使用できません。
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#define MAX_THREADS 3
#define BUF_SIZE 255
DWORD WINAPI MyThreadFunction( LPVOID lpParam );
void ErrorHandler(LPCTSTR lpszFunction);
// Sample custom data structure for threads to use.
// This is passed by void pointer so it can be any data type
// that can be passed using a single void pointer (LPVOID).
typedef struct MyData {
int val1;
int val2;
} MYDATA, *PMYDATA;
int _tmain()
{
PMYDATA pDataArray[MAX_THREADS];
DWORD dwThreadIdArray[MAX_THREADS];
HANDLE hThreadArray[MAX_THREADS];
// Create MAX_THREADS worker threads.
for( int i=0; i<MAX_THREADS; i++ )
{
// Allocate memory for thread data.
pDataArray[i] = (PMYDATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(MYDATA));
if( pDataArray[i] == NULL )
{
// If the array allocation fails, the system is out of memory
// so there is no point in trying to print an error message.
// Just terminate execution.
ExitProcess(2);
}
// Generate unique data for each thread to work with.
pDataArray[i]->val1 = i;
pDataArray[i]->val2 = i+100;
// Create the thread to begin execution on its own.
hThreadArray[i] = CreateThread(
NULL, // default security attributes
0, // use default stack size
MyThreadFunction, // thread function name
pDataArray[i], // argument to thread function
0, // use default creation flags
&dwThreadIdArray[i]); // returns the thread identifier
// Check the return value for success.
// If CreateThread fails, terminate execution.
// This will automatically clean up threads and memory.
if (hThreadArray[i] == NULL)
{
ErrorHandler(TEXT("CreateThread"));
ExitProcess(3);
}
} // End of main thread creation loop.
// Wait until all threads have terminated.
WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);
// Close all thread handles and free memory allocations.
for(int i=0; i<MAX_THREADS; i++)
{
CloseHandle(hThreadArray[i]);
if(pDataArray[i] != NULL)
{
HeapFree(GetProcessHeap(), 0, pDataArray[i]);
pDataArray[i] = NULL; // Ensure address is not reused.
}
}
return 0;
}
DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
HANDLE hStdout;
PMYDATA pDataArray;
TCHAR msgBuf[BUF_SIZE];
size_t cchStringSize;
DWORD dwChars;
// Make sure there is a console to receive output results.
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if( hStdout == INVALID_HANDLE_VALUE )
return 1;
// Cast the parameter to the correct data type.
// The pointer is known to be valid because
// it was checked for NULL before the thread was created.
pDataArray = (PMYDATA)lpParam;
// Print the parameter values using thread-safe functions.
StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d\n"),
pDataArray->val1, pDataArray->val2);
StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
WriteConsole(hStdout, msgBuf, (DWORD)cchStringSize, &dwChars, NULL);
return 0;
}
void ErrorHandler(LPCTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code.
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message.
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR) lpMsgBuf) + lstrlen((LPCTSTR) lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR) lpDisplayBuf, TEXT("Error"), MB_OK);
// Free error-handling buffer allocations.
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
関数の MyThreadFunction
多くはスレッド セーフではなく、特にマルチスレッド CRT を使用していない場合は、C ランタイム ライブラリ (CRT) の使用を回避します。 関数で ThreadProc
CRT を使用する場合は、代わりに _beginthreadex 関数を使用します。
ポインターが無効になるため、作成中のスレッドが新しいスレッドの前に終了した場合、ローカル変数のアドレスを渡すリスクがあります。 代わりに、動的に割り当てられたメモリへのポインターを渡すか、作成中のスレッドが新しいスレッドが終了するまで待機させます。 グローバル変数を使用して、作成中のスレッドから新しいスレッドにデータを渡すこともできます。 グローバル変数では、通常、複数のスレッドによるアクセスを同期する必要があります。 同期の詳細については、「 複数スレッドの実行の同期」を参照してください。
作成スレッドでは、 引数を使用して CreateThread を使用して、以下を指定できます。
- 新しいスレッドへのハンドルのセキュリティ属性。 これらのセキュリティ属性には、子プロセスによってハンドルを継承できるかどうかを決定する継承フラグが含まれます。 セキュリティ属性にはセキュリティ記述子も含まれます。この記述子は、アクセスが許可される前に、システムがスレッドのハンドルの後続のすべての使用に対してアクセス チェックを実行するために使用します。
- 新しいスレッドの初期スタック サイズ。 スレッドのスタックは、プロセスのメモリ空間に自動的に割り当てられます。システムは必要に応じてスタックを増やし、スレッドが終了したときにスタックを解放します。 詳細については、「 スレッド スタック サイズ」を参照してください。
- 中断状態でスレッドを作成できるようにする作成フラグ。 中断されると、 ResumeThread 関数が呼び出されるまでスレッドは実行されません。
CreateRemoteThread 関数を呼び出してスレッドを作成することもできます。 この関数は、デバッグ中のプロセスのアドレス空間で実行されるスレッドを作成するためにデバッガー プロセスによって使用されます。
関連トピック