• vc++高级班之多线程篇[7]---线程间的同步机制②


    //示例代码:
    CStringArray g_ArrString;
    UINT __cdecl ThreadProc(LPVOID lpParameter)
    {
     int startIdx = (int)lpParameter;
     for (int idx = startIdx; idx < startIdx+100; ++idx) {
      CString str;
      str.Format(_T("%d"), idx);
      g_ArrString.Add(str);
     }
     return 0;
    }
     
    void CThreadTestDlg::OnBnClickedBtn()
    {
     for (int idx = 1; idx <= 50; ++idx) {
      AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
     }
    }
     
    void CThreadTestDlg::OnBnClickedPrintBtn()
    {
     CString strCount;
     INT_PTR nCount = g_ArrString.GetCount();
     strCount.Format(_T("%d"), nCount);
     MessageBox(strCount);
     
     for (INT_PTR idx = 0; idx < nCount; ++idx) {
      OutputDebugString(g_ArrString.GetAt(idx));
     }
    }
     
    ///////////////////////////////////////////////////////////////////////////////
     
    ①、Mutex(互斥器)
     
    使用方法:
    1、创建一个互斥器:CreateMutex;
    2、打开一个已经存在的互斥器:OpenMutex;
    3、获得互斥器的拥有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……(可能造成阻塞);
    4、释放互斥器的拥有权:ReleaseMutex;
    5、关闭互斥器:CloseHandle;
     
    HANDLE ghMutex = NULL;
    CStringArray g_ArrString;
    UINT __cdecl ThreadProc(LPVOID lpParameter)
    {
     int startIdx = (int)lpParameter;
     for (int idx = startIdx; idx < startIdx+100; ++idx) {
      CString str;
      str.Format(_T("%d"), idx);
     
      DWORD dwWaitResult = WaitForSingleObject(ghMutex, INFINITE);
      switch (dwWaitResult)
      {
      case WAIT_ABANDONED:
      case WAIT_OBJECT_0:
       g_ArrString.Add(str);
       ReleaseMutex(ghMutex);
       break;
      }
      //g_ArrString.Add(str);
     }
     return 0;
    }
     
    void CThreadTestDlg::OnBnClickedBtn()
    {
     ghMutex = CreateMutex(NULL, FALSE, NULL);
     for (int idx = 1; idx <= 50; ++idx) {
      AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
     }
    }
     
    void CThreadTestDlg::OnBnClickedPrintBtn()
    {
     CString strCount;
     INT_PTR nCount = g_ArrString.GetCount();
     strCount.Format(_T("%d"), nCount);
     MessageBox(strCount);
     
     for (INT_PTR idx = 0; idx < nCount; ++idx) {
      OutputDebugString(g_ArrString.GetAt(idx));
     }
     
     CloseHandle(ghMutex);
    }
     
    ※ 命名标准:Mutex 可以跨进程使用,所以其名称对整个系统而言是全局的,所以命名不要过于普通,类似:Mutex、Object 等。
    最好想一些独一无二的名字等!
     
    固有特点(优点+缺点):
    1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
    2、因为是核心对象,所以执行速度会比 Critical Sections 慢几乎100倍的时间(当然只是相比较而言);
    3、因为是核心对象,而且可以命名,所以可以跨进程使用;
    4、Mutex 使用正确的情况下不会发生死锁;
    5、在“等待”一个 Mutex 的时候,可以指定“结束等待”的时间长度;
    6、可以检测到当前拥有互斥器所有权的线程是否已经退出!Wait……函数会返回:WAIT_ABANDONED
    ===================================================
    ②、Semaphores(信号量)
     
    租车例子的比喻很恰当!
     
    使用方法:
    1、创建一个信号量:CreateSemaphore;
    2、打开一个已经存在的信号量:OpenSemaphore;
    3、获得信号量的一个占有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……(可能造成阻塞);
    4、释放信号量的占有权:ReleaseSemaphore;
    5、关闭信号量:CloseHandle;
     
    HANDLE ghSemaphore = NULL;
    UINT __cdecl ThreadProc(LPVOID lpParameter)
    {
     int startIdx = (int)lpParameter;
     CString strOut;
     while(TRUE) {
     
      DWORD dwWaitResult = WaitForSingleObject(ghSemaphore, 0);
      switch (dwWaitResult)
      {
      case WAIT_OBJECT_0:
       strOut.Format(_T("Thread %d: wait succeeded !"), GetCurrentThreadId());
       OutputDebugString(strOut);
       ReleaseSemaphore(ghSemaphore, 1, NULL);
     
       break;
      case WAIT_TIMEOUT:
       strOut.Format(_T("Thread %d: wait timed out !"), GetCurrentThreadId());
       OutputDebugString(strOut);
       break;
      }
     }
     return 0;
    }
     
    void CThreadTestDlg::OnBnClickedBtn()
    {
     ghSemaphore = CreateSemaphore(NULL, 10, 10, NULL);
     for (int idx = 1; idx <= 20; ++idx) {
      AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
     }
    }
     
    void CThreadTestDlg::OnBnClickedPrintBtn()
    {
     CloseHandle(ghSemaphore);
    }
     
    ※ 命名标准:Semaphores 可以跨进程使用,所以其名称对整个系统而言是全局的,所以命名不要过于普通,类似:Semaphore、Object 等。
    最好想一些独一无二的名字等!
     
    固有特点(优点+缺点):
    1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
    2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
    3、因为是核心对象,而且可以命名,所以可以跨进程使用;
    4、Semaphore 使用正确的情况下不会发生死锁;
    5、在“等待”一个 信号量 的时候,可以指定“结束等待”的时间长度;
    6、非排他性的占有,跟 Critical Sections 和 Mutex 不同,这两种而言是排他性占有,
    即:同一时间内只能有单一线程获得目标并拥有操作的权利,而 Semaphores 则不是这样,
    同一时间内可以有多个线程获得目标并操作!
     
    所以,这里面问大家一个问题,如果还是用信号量的方式去做之前向 CStringArray 中添加节点的同步可以吗?
    ===================================================
    ③、Event Objects(事件)
     
     Event 方式是最具弹性的同步机制,因为他的状态完全由你去决定,不会像 Mutex 和 Semaphores 的状态会由类似:
     WaitForSingleObject 一类的函数的调用而改变,所以你可以精确的告诉 Event 对象该做什么事?以及什么时候去做!
     
     使用方法:
    1、创建一个事件对象:CreateEvent;
    2、打开一个已经存在的事件对象:OpenEvent;
    3、获得事件的占有权:WaitForSingleObject 等函数(可能造成阻塞);
    4、释放事件的占有权(设置为激发(有信号)状态,以让其他等待中的线程苏醒):SetEvent;
    5、手动置为非激发(无信号)状态:ResetEvent
    6、关闭事件对象句柄:CloseHandle;
     
    固有特点(优点+缺点):
    1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
    2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
    3、因为是核心对象,而且可以命名,所以可以跨进程使用;
    4、通常被用于 overlapped I/O 或被用来设计某些自定义的同步对象。
     
    #include <windows.h>
    #include <stdio.h>
     
    #define THREADCOUNT 4
     
    HANDLE ghGlobalWriteEvent;
    HANDLE ghReadEvents[THREADCOUNT];
     
    DWORD WINAPI ThreadProc(LPVOID);
     
    void CreateEventsAndThreads(void)
    {
        HANDLE hThread;
        DWORD i, dwThreadID;
     
        // Create a manual-reset event object. The master thread sets
        // this to nonsignaled when it writes to the shared buffer.
     
        ghGlobalWriteEvent = CreateEvent(
            NULL, // default security attributes
            TRUE, // manual-reset event
            TRUE, // initial state is signaled
            TEXT("WriteEvent") // object name
            );
     
        if (ghGlobalWriteEvent == NULL)
        {
            printf("CreateEvent failed (%d) ", GetLastError());
            return;
        }
        else if ( GetLastError() == ERROR_ALREADY_EXISTS )
        {
            printf("Named event already exists. ");
            return;
        }
     
        // Create multiple threads and an auto-reset event object
        // for each thread. Each thread sets its event object to
        // signaled when it is not reading from the shared buffer.
     
        for(i = 0; i < THREADCOUNT; i++)
        {
            // Create the auto-reset event
            ghReadEvents[i] = CreateEvent(
                NULL, // no security attributes
                FALSE, // auto-reset event
                TRUE, // initial state is signaled
                NULL); // object not named
     
            if (ghReadEvents[i] == NULL)
            {
                printf("CreateEvent failed (%d) ", GetLastError());
                return;
            }
     
            hThread = CreateThread(NULL,
                0,
                ThreadProc,
                &ghReadEvents[i], // pass event handle
                0,
                &dwThreadID);
     
            if (hThread == NULL)
            {
                printf("CreateThread failed (%d) ", GetLastError());
                return;
            }
        }
    }
     
    void WriteToBuffer(VOID)
    {
        DWORD dwWaitResult, i;
     
        // Reset ghGlobalWriteEvent to nonsignaled, to block readers
     
        if (! ResetEvent(ghGlobalWriteEvent) )
        {
            printf("ResetEvent failed (%d) ", GetLastError());
            return;
        }
     
        // Wait for all reading threads to finish reading
     
        dwWaitResult = WaitForMultipleObjects(
            THREADCOUNT, // number of handles in array
            ghReadEvents, // array of read-event handles
            TRUE, // wait until all are signaled
            INFINITE); // indefinite wait
     
        switch (dwWaitResult)
        {
            // All read-event objects were signaled
            case WAIT_OBJECT_0:
                // TODO: Write to the shared buffer
                printf("Main thread writing to the shared buffer... ");
                break;
     
            // An error occurred
            default:
                printf("Wait error: %d ", GetLastError());
                ExitProcess(0);
        }
     
        // Set ghGlobalWriteEvent to signaled
     
        if (! SetEvent(ghGlobalWriteEvent) )
        {
            printf("SetEvent failed (%d) ", GetLastError());
            ExitProcess(0);
        }
     
        // Set all read events to signaled
        for(i = 0; i < THREADCOUNT; i++)
            if (! SetEvent(ghReadEvents[i]) )
            {
                printf("SetEvent failed (%d) ", GetLastError());
                return;
            }
    }
     
    void CloseEvents()
    {
        int i;
     
        for( i=0; i < THREADCOUNT; i++ )
            CloseHandle(ghReadEvents[i]);
     
        CloseHandle(ghGlobalWriteEvent);
    }
     
    void main()
    {
        int i;
     
        // TODO: Create the shared buffer
     
        // Create the events and THREADCOUNT threads to read from the buffer
     
        CreateEventsAndThreads();
     
        // Write to the buffer three times, just for test purposes
     
        for(i=0; i < 3; i++)
            WriteToBuffer();
     
        // Close the events
     
        CloseEvents();
    }
     
    DWORD WINAPI ThreadProc(LPVOID lpParam)
    {
        DWORD dwWaitResult;
        HANDLE hEvents[2];
     
        hEvents[0] = *(HANDLE*)lpParam; // thread's read event
        hEvents[1] = ghGlobalWriteEvent; // global write event
     
        dwWaitResult = WaitForMultipleObjects(
            2, // number of handles in array
            hEvents, // array of event handles
            TRUE, // wait till all are signaled
            INFINITE); // indefinite wait
     
        switch (dwWaitResult)
        {
            // Both event objects were signaled
            case WAIT_OBJECT_0:
                // TODO: Read from the shared buffer
                printf("Thread %d reading from buffer... ",
                       GetCurrentThreadId());
                break;
     
            // An error occurred
            default:
                printf("Wait error: %d ", GetLastError());
                ExitThread(0);
        }
     
        // Set the read event to signaled
     
        if (! SetEvent(hEvents[0]) )
        {
            printf("SetEvent failed (%d) ", GetLastError());
            ExitThread(0);
        }
     
        return 1;
    }
     
    ===================================================
    ※※※ 小作业:
    MFC中同样对 Mutex、Semaphores、Event 进行了封装,所以尝试使用 CMutex、CSemaphore、CEvent 等类进行线程间的同步将更为方便!
    ------------------------------------- End -------------------------------------------
  • 相关阅读:
    Redis在win7上的可视化应用
    MemCache在win7上的可视化配置以及Nodejs/Net应用
    如何让nodejs同步操作
    Asp.Net Web API 2第十四课——Content Negotiation(内容协商)
    Redis for Windows(C#缓存)配置文件详解
    Redis for Windows(C#缓存)安装和使用
    Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化
    Asp.Net Web API 2第十二课——Media Formatters媒体格式化器
    Asp.Net Web API 2第十一课——在Web API中使用Dependency Resolver
    Asp.Net Web API 2第十课——使用OWIN自承载Web API
  • 原文地址:https://www.cnblogs.com/liaocheng/p/4243449.html
Copyright © 2020-2023  润新知