C/C++多线程API详解

文章目录

  • 1.多线程并发和同步
  • 2.CreateThread
  • 3.SetEvent
  • 4.WaitForSingleObject
  • 5.WaitForMultipleObjects
  • 6.CloseHandle
  • 7.例子
    • case1: CreateThread
    • case2: SetEvent
    • case3: WaitForMultipleObjects
    • case4: WaitForMultipleObjects
    • case6: CloseHandle

1.多线程并发和同步

如果一个单一程序中,有些步骤可以同时进行,则可以把这个单一程序拆分成可以并发执行的几个部分。显著提升程序处理数据的效率。

但多个线程同时执行,一般会面临多个线程间的同步问题。比如,线程之间有依赖关系,一个线程需要另外一个线程的处理结果,或者多个线程间竞争共享资源。

本文给大家详细讲解一下多线程的API,并在最后给出一个例子。

2.CreateThread

原型和参数:

HANDLE CreateThread(
  [in, optional]  LPSECURITY_ATTRIBUTES   lpThreadAttributes,//确定返回的句柄是否可以被子进程继承的结构。 如果 lpThreadAttributes 为 NULL,句柄不能被继承。 
  [in]            SIZE_T                  dwStackSize,//堆栈的初始大小,以字节为单位。 系统将此值四舍五入到最近的页面。 如果此参数为零,则新线程使用可执行文件的默认大小。
  [in]            LPTHREAD_START_ROUTINE  lpStartAddress,//指向要由线程执行的应用程序定义函数的指针。 该指针表示线程的起始地址。这个参数很重要!!!
  [in, optional]  __drv_aliasesMem LPVOID lpParameter,//指向要传递给线程的变量的指针
  [in]            DWORD                   dwCreationFlags,//v控制线程创建的标志,为0,则:线程在创建后立即运行。为CREATE_SUSPENDED,则:线程在挂起状态下创建,直到 调用 ResumeThread 函数。 为STACK_SIZE_PARAM_IS_A_RESERVATION ,则:dwStackSize 参数指定堆栈的初始保留大小。 如果未指定此标志,则 dwStackSize 指定提交大小。
  [out, optional] LPDWORD                 lpThreadId //指向接收线程标识符的变量的指针。 如果这个参数是 NULL ,不返回线程标识符。 
);

CreateThread 函数为进程创建一个新线程 创建线程必须指定新线程要执行的代码的起始地址。 通常,起始地址是程序代码中定义的函数的名称。

返回值:

如果函数成功,则返回值是新线程的句柄。
如果函数失败,则返回值为 NULL 。

3.SetEvent

原型和参数:

BOOL SetEvent(
  [in] HANDLE hEvent //事件对象的句柄。The CreateEvent 或者 OpenEvent函数返回这种句柄
);

将指定的事件对象设置为信号状态。

返回值:
如果函数成功,则返回值非零。
如果函数失败,则返回值为零。

4.WaitForSingleObject

原型和参数:

DWORD WaitForSingleObject(
  [in] HANDLE hHandle, //对象的句柄
  [in] DWORD  dwMilliseconds //超时间隔,以毫秒为单位。 如果指定了非零值,则函数会一直等待,直到对象发出信号或间隔过去。 如果 dwMilliseconds 为零,则如果对象未发出信号,则函数不会进入等待状态; 它总是立即返回。 如果 dwMilliseconds 是 INFINITE ,则该函数将仅在对象发出信号时返回。 
);

等待直到指定的对象处于信号状态或超时间隔过去。

返回值:

WAIT_ABANDONED :指定的对象是一个互斥对象,在拥有该互斥对象的线程终止之前,该对象没有被拥有该对象的线程释放。 互斥对象的所有权被授予调用线程,并且互斥状态设置为无信号。

WAIT_OBJECT_0 :指定对象的状态被通知。

WAIT_TIMEOUT :超时间隔已过,对象的状态为无信号状态

WAIT_FAILED :功能失败

5.WaitForMultipleObjects

原型和参数:

DWORD WaitForMultipleObjects(
  [in] DWORD        nCount, //指向的数组中对象句柄的数量. 此参数不能为零
  [in] const HANDLE *lpHandles, //对象句柄数组
  [in] BOOL         bWaitAll, //如果此参数为 TRUE 中所有对象的状态发出信号时返回 lpHandles 数组 如果 FALSE ,则当任何一个对象的状态设置为已发出信号时,该函数将返回。 在后一种情况下,返回值指示其状态导致函数返回的对象。 
  [in] DWORD        dwMilliseconds //超时间隔,以毫秒为单位。 如果指定了非零值,则函数将一直等待,直到指定的对象收到信号或间隔过去。 如果 dwMilliseconds 为零,如果指定的对象没有发出信号,则函数不会进入等待状态; 它总是立即返回。 如果 dwMilliseconds 是 INFINITE ,则该函数将仅在指定对象发出信号时返回。 
);

等待直到一个或所有指定对象处于信号状态或超时间隔过去。

返回值:

WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1):如果 bWaitAll 为 TRUE ,则指定范围内的返回值指示所有指定对象的状态都已发出信号。

如果 bWaitAll 为 FALSE ,则返回值减去 WAIT_OBJECT_0 表示满足等待的对象的 lpHandles 数组索引。 如果在调用期间有多个对象发出信号,则这是所有已发出信号对象中索引值最小的已发出信号对象的数组索引。

WAIT_ABANDONED_0 到 ( WAIT_ABANDONED_0 + nCount – 1):如果 bWaitAll 为 TRUE ,则指定范围内的返回值指示所有指定对象的状态都已发出信号,并且至少有一个对象是废弃的互斥对象。

如果 bWaitAll 为 FALSE ,则返回值减去 WAIT_ABANDONED_0 表示满足等待的废弃互斥对象的 lpHandles 数组索引。 互斥对象的所有权被授予调用线程,并且互斥对象被设置为无信号。

WAIT_TIMEOUT :超时间隔已过 bWaitAll 参数指定的条件。

WAIT_FAILED::功能失败

6.CloseHandle

原型和参数:

BOOL CloseHandle(
  [in] HANDLE hObject //打开对象的有效句柄
);

关闭打开的对象句柄。

CloseHandle 函数关闭以下对象的句柄:

访问令牌
通讯设备
控制台输入
控制台屏幕缓冲区
事件
文件
文件映射
I/O 完成端口
工作
中转槽
内存资源通知
互斥体
命名管道
管道
过程
信号
线
交易
等待定时器

返回值:

如果函数成功,则返回值非零。
如果函数失败,则返回值为零。

7.例子

case1: CreateThread

#include 
#include 
#include 

#define MAX_THREADS 3
#define BUF_SIZE 255

DWORD WINAPI MyThreadFunction( LPVOID lpParam );
void ErrorHandler(LPTSTR 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(LPTSTR 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);
}

case2: SetEvent

#include 
#include 

#define THREADCOUNT 4 

HANDLE ghWriteEvent; 
HANDLE ghThreads[THREADCOUNT];

DWORD WINAPI ThreadProc(LPVOID);

void CreateEventsAndThreads(void) 
{
    int i; 
    DWORD dwThreadID; 

    // Create a manual-reset event object. The write thread sets this
    // object to the signaled state when it finishes writing to a 
    // shared buffer. 

    ghWriteEvent = CreateEvent( 
        NULL,               // default security attributes
        TRUE,               // manual-reset event
        FALSE,              // initial state is nonsignaled
        TEXT("WriteEvent")  // object name
        ); 

    if (ghWriteEvent == NULL) 
    { 
        printf("CreateEvent failed (%d)\n", GetLastError());
        return;
    }

    // Create multiple threads to read from the buffer.

    for(i = 0; i < THREADCOUNT; i++) 
    {
        // TODO: More complex scenarios may require use of a parameter
        //   to the thread procedure, such as an event per thread to  
        //   be used for synchronization.
        ghThreads[i] = CreateThread(
            NULL,              // default security
            0,                 // default stack size
            ThreadProc,        // name of the thread function
            NULL,              // no thread parameters
            0,                 // default startup flags
            &dwThreadID); 

        if (ghThreads[i] == NULL) 
        {
            printf("CreateThread failed (%d)\n", GetLastError());
            return;
        }
    }
}

void WriteToBuffer(VOID) 
{
    // TODO: Write to the shared buffer.
    
    printf("Main thread writing to the shared buffer...\n");

    // Set ghWriteEvent to signaled

    if (! SetEvent(ghWriteEvent) ) 
    {
        printf("SetEvent failed (%d)\n", GetLastError());
        return;
    }
}

void CloseEvents()
{
    // Close all event handles (currently, only one global handle).
    
    CloseHandle(ghWriteEvent);
}

int main( void )
{
    DWORD dwWaitResult;

    // TODO: Create the shared buffer

    // Create events and THREADCOUNT threads to read from the buffer

    CreateEventsAndThreads();

    // At this point, the reader threads have started and are most
    // likely waiting for the global event to be signaled. However, 
    // it is safe to write to the buffer because the event is a 
    // manual-reset event.
    
    WriteToBuffer();

    printf("Main thread waiting for threads to exit...\n");

    // The handle for each thread is signaled when the thread is
    // terminated.
    dwWaitResult = WaitForMultipleObjects(
        THREADCOUNT,   // number of handles in array
        ghThreads,     // array of thread handles
        TRUE,          // wait until all are signaled
        INFINITE);

    switch (dwWaitResult) 
    {
        // All thread objects were signaled
        case WAIT_OBJECT_0: 
            printf("All threads ended, cleaning up for application exit...\n");
            break;

        // An error occurred
        default: 
            printf("WaitForMultipleObjects failed (%d)\n", GetLastError());
            return 1;
    } 
            
    // Close the events to clean up

    CloseEvents();

    return 0;
}

DWORD WINAPI ThreadProc(LPVOID lpParam) 
{
    // lpParam not used in this example.
    UNREFERENCED_PARAMETER(lpParam);

    DWORD dwWaitResult;

    printf("Thread %d waiting for write event...\n", GetCurrentThreadId());
    
    dwWaitResult = WaitForSingleObject( 
        ghWriteEvent, // event handle
        INFINITE);    // indefinite wait

    switch (dwWaitResult) 
    {
        // Event object was signaled
        case WAIT_OBJECT_0: 
            //
            // TODO: Read from the shared buffer
            //
            printf("Thread %d reading from buffer\n", 
                   GetCurrentThreadId());
            break; 

        // An error occurred
        default: 
            printf("Wait error (%d)\n", GetLastError()); 
            return 0; 
    }

    // Now that we are done reading the buffer, we could use another
    // event to signal that this thread is no longer reading. This
    // example simply uses the thread handle for synchronization (the
    // handle is signaled when the thread terminates.)

    printf("Thread %d exiting\n", GetCurrentThreadId());
    return 1;
}

case3: WaitForMultipleObjects

#include 
#include 

#define THREADCOUNT 2

HANDLE ghMutex; 

DWORD WINAPI WriteToDatabase( LPVOID );

int main( void )
{
    HANDLE aThread[THREADCOUNT];
    DWORD ThreadID;
    int i;

    // Create a mutex with no initial owner

    ghMutex = CreateMutex( 
        NULL,              // default security attributes
        FALSE,             // initially not owned
        NULL);             // unnamed mutex

    if (ghMutex == NULL) 
    {
        printf("CreateMutex error: %d\n", GetLastError());
        return 1;
    }

    // Create worker threads

    for( i=0; i < THREADCOUNT; i++ )
    {
        aThread[i] = CreateThread( 
                     NULL,       // default security attributes
                     0,          // default stack size
                     (LPTHREAD_START_ROUTINE) WriteToDatabase, 
                     NULL,       // no thread function arguments
                     0,          // default creation flags
                     &ThreadID); // receive thread identifier

        if( aThread[i] == NULL )
        {
            printf("CreateThread error: %d\n", GetLastError());
            return 1;
        }
    }

    // Wait for all threads to terminate

    WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);

    // Close thread and mutex handles

    for( i=0; i < THREADCOUNT; i++ )
        CloseHandle(aThread[i]);

    CloseHandle(ghMutex);

    return 0;
}

DWORD WINAPI WriteToDatabase( LPVOID lpParam )
{ 
    // lpParam not used in this example
    UNREFERENCED_PARAMETER(lpParam);

    DWORD dwCount=0, dwWaitResult; 

    // Request ownership of mutex.

    while( dwCount < 20 )
    { 
        dwWaitResult = WaitForSingleObject( 
            ghMutex,    // handle to mutex
            INFINITE);  // no time-out interval
 
        switch (dwWaitResult) 
        {
            // The thread got ownership of the mutex
            case WAIT_OBJECT_0: 
                __try { 
                    // TODO: Write to the database
                    printf("Thread %d writing to database...\n", 
                            GetCurrentThreadId());
                    dwCount++;
                } 

                __finally { 
                    // Release ownership of the mutex object
                    if (! ReleaseMutex(ghMutex)) 
                    { 
                        // Handle error.
                    } 
                } 
                break; 

            // The thread got ownership of an abandoned mutex
            // The database is in an indeterminate state
            case WAIT_ABANDONED: 
                return FALSE; 
        }
    }
    return TRUE; 
}

case4: WaitForMultipleObjects

#include 
#include 

HANDLE ghEvents[2];

DWORD WINAPI ThreadProc( LPVOID );

int main( void )
{
    HANDLE hThread; 
    DWORD i, dwEvent, dwThreadID; 

    // Create two event objects

    for (i = 0; i < 2; i++) 
    { 
        ghEvents[i] = CreateEvent( 
            NULL,   // default security attributes
            FALSE,  // auto-reset event object
            FALSE,  // initial state is nonsignaled
            NULL);  // unnamed object

        if (ghEvents[i] == NULL) 
        { 
            printf("CreateEvent error: %d\n", GetLastError() ); 
            ExitProcess(0); 
        } 
    } 

    // Create a thread

    hThread = CreateThread( 
                 NULL,         // default security attributes
                 0,            // default stack size
                 (LPTHREAD_START_ROUTINE) ThreadProc, 
                 NULL,         // no thread function arguments
                 0,            // default creation flags
                 &dwThreadID); // receive thread identifier

    if( hThread == NULL )
    {
        printf("CreateThread error: %d\n", GetLastError());
        return 1;
    }

    // Wait for the thread to signal one of the event objects

    dwEvent = WaitForMultipleObjects( 
        2,           // number of objects in array
        ghEvents,     // array of objects
        FALSE,       // wait for any object
        5000);       // five-second wait

    // The return value indicates which event is signaled

    switch (dwEvent) 
    { 
        // ghEvents[0] was signaled
        case WAIT_OBJECT_0 + 0: 
            // TODO: Perform tasks required by this event
            printf("First event was signaled.\n");
            break; 

        // ghEvents[1] was signaled
        case WAIT_OBJECT_0 + 1: 
            // TODO: Perform tasks required by this event
            printf("Second event was signaled.\n");
            break; 

        case WAIT_TIMEOUT:
            printf("Wait timed out.\n");
            break;

        // Return value is invalid.
        default: 
            printf("Wait error: %d\n", GetLastError()); 
            ExitProcess(0); 
    }

    // Close event handles

    for (i = 0; i < 2; i++) 
        CloseHandle(ghEvents[i]); 
    
    return 0;   
}

DWORD WINAPI ThreadProc( LPVOID lpParam )
{

    // lpParam not used in this example
    UNREFERENCED_PARAMETER( lpParam);

    // Set one event to the signaled state

    if ( !SetEvent(ghEvents[0]) ) 
    {
        printf("SetEvent failed (%d)\n", GetLastError());
        return 1;
    }
    return 0;
}

case6: CloseHandle

dwPriorityClass = 0;
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID );
if( hProcess == NULL )
	printError( TEXT("OpenProcess") );
else
{
	dwPriorityClass = GetPriorityClass( hProcess );
	if( !dwPriorityClass )
	printError( TEXT("GetPriorityClass") );
	CloseHandle( hProcess );
}

你可能感兴趣的:(C/C++,多线程,CreateEvent,WaitForSingleOb,WaitForMultiple,CloseHandle)