Membuat Utas
Fungsi CreateThread membuat utas baru untuk proses. Utas yang membuat harus menentukan alamat awal dari kode yang akan dijalankan oleh utas baru tersebut. Biasanya, alamat awal adalah nama fungsi yang ditentukan dalam kode program (untuk informasi selengkapnya, lihat ThreadProc). Fungsi ini mengambil satu parameter dan mengembalikan nilai DWORD. Proses dapat memiliki beberapa utas menjalankan fungsi yang sama secara bersamaan.
Berikut ini adalah contoh sederhana yang menunjukkan cara membuat thread baru yang menjalankan fungsi yang didefinisikan secara lokal, MyThreadFunction
.
Utas panggilan menggunakan fungsiWaitForMultipleObjects untuk bertahan hingga semua utas pekerja dihentikan. Utas panggilan diblokir saat sedang menunggu; untuk melanjutkan pemrosesan, utas panggilan akan menggunakan WaitForSingleObject dan menunggu setiap utas pekerja untuk memberi sinyal pada objek tunggunya. Perhatikan bahwa jika Anda menutup handle pada thread pekerja sebelum itu dihentikan, ini tidak akan mengakhiri thread pekerja tersebut. Namun, handle tidak akan tersedia dalam panggilan fungsi berikutnya.
#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);
}
Fungsi MyThreadFunction
menghindari penggunaan perpustakaan waktu proses C (CRT), karena banyak fungsinya tidak aman untuk penggunaan bersama di utas, terutama jika Anda tidak menggunakan CRT multithreaded. Jika Anda ingin menggunakan CRT dalam fungsi ThreadProc
, gunakan fungsi _beginthreadex sebagai gantinya.
Sangat berisiko untuk meneruskan alamat variabel lokal jika utas yang membuat berhenti sebelum utas baru, karena pointer menjadi tidak valid. Sebagai gantinya, teruskan penunjuk ke memori yang dialokasikan secara dinamis atau buat utas pembuatan menunggu utas baru dihentikan. Data juga dapat diteruskan dari utas pembuatan ke utas baru menggunakan variabel global. Dengan variabel global, biasanya perlu untuk menyinkronkan akses dengan beberapa utas. Untuk informasi lebih lanjut tentang sinkronisasi, lihat Menyinkronkan Eksekusi Beberapa Utas.
Alur pembuatan dapat menggunakan argumen untuk CreateThread untuk menentukan hal berikut:
- Atribut keamanan untuk pegangan ke benang baru. Atribut keamanan ini mencakup flag pewarisan yang menentukan apakah handle dapat diwariskan oleh proses turunan. Atribut keamanan juga menyertakan penjabaran keamanan, yang digunakan sistem untuk melakukan pemeriksaan akses pada semua penggunaan pegangan utas berikutnya sebelum akses diberikan.
- Ukuran tumpukan awal utas baru. Tumpukan utas dialokasikan secara otomatis di ruang memori proses; sistem menambah ukuran tumpukan sesuai kebutuhan dan membebaskannya saat utas berakhir. Untuk informasi selengkapnya, lihat Ukuran Tumpukan Utas.
- Tanda kreasi yang memungkinkan Anda membuat utas dalam keadaan tertunda. Saat ditangguhkan, utas tidak berjalan hingga fungsi ResumeThread dipanggil.
Anda juga bisa membuat utas dengan memanggil fungsi CreateRemoteThread. Fungsi ini digunakan oleh proses debugger untuk membuat utas yang berjalan di ruang alamat proses yang sedang di-debug.