Membuat Utas

Fungsi CreateThread membuat utas baru untuk proses. Alur pembuatan harus menentukan alamat awal kode yang akan dijalankan utas baru. 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 secara bersamaan menjalankan fungsi yang sama.

Berikut ini adalah contoh sederhana yang menunjukkan cara membuat utas baru yang menjalankan fungsi yang ditentukan secara lokal, MyThreadFunction.

Utas panggilan menggunakan fungsi WaitForMultipleObjects untuk bertahan hingga semua utas pekerja telah dihentikan. Alur panggilan memblokir saat menunggu; untuk melanjutkan pemrosesan, utas panggilan akan menggunakan WaitForSingleObject dan menunggu setiap utas pekerja memberi sinyal objek tunggunya. Perhatikan bahwa jika Anda menutup handel ke utas pekerja sebelum dihentikan, ini tidak mengakhiri utas pekerja. Namun, handel tidak akan tersedia untuk digunakan 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 ini MyThreadFunction menghindari penggunaan pustaka run-time C (CRT), karena banyak fungsinya tidak aman untuk utas, terutama jika Anda tidak menggunakan CRT multithreaded. Jika Anda ingin menggunakan CRT dalam ThreadProc fungsi, gunakan fungsi _beginthreadex sebagai gantinya.

Sangat berisiko untuk meneruskan alamat variabel lokal jika alur pembuatan keluar sebelum utas baru, karena penunjuk 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 oleh beberapa utas. Untuk informasi selengkapnya tentang sinkronisasi, lihat Menyinkronkan Eksekusi Beberapa Utas.

Alur pembuatan dapat menggunakan argumen untuk CreateThread untuk menentukan hal berikut:

  • Atribut keamanan untuk handel ke utas baru. Atribut keamanan ini mencakup bendera pewarisan yang menentukan apakah handel dapat diwariskan oleh proses anak. Atribut keamanan juga menyertakan deskriptor keamanan, yang digunakan sistem untuk melakukan pemeriksaan akses pada semua penggunaan handel utas berikutnya sebelum akses diberikan.
  • Ukuran tumpukan awal utas baru. Tumpukan utas dialokasikan secara otomatis di ruang memori proses; sistem meningkatkan tumpukan sesuai kebutuhan dan membebaskannya ketika utas berakhir. Untuk informasi selengkapnya, lihat Ukuran Tumpukan Utas.
  • Bendera pembuatan yang memungkinkan Anda membuat utas dalam status ditangguhkan. Saat ditangguhkan, utas tidak berjalan hingga fungsi ResumeThread dipanggil.

Anda juga dapat 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.

Mengakhiri Utas