音視頻同步系列文章之—–Windows同步機制

來源:互聯網
上載者:User

     同步的意思是,保證一個程式在被不適宜的切換時,不會出現問題。

    對Window3.1來講,雖然有多任務,但是沒有同步基層。因為這些多任務的協作是通過調用API函數,比如(GetMessage和PeekMessage)來實現。如果一個程式調用了GetMessage或PeekMessage,則意思就是說,我現在處於可中斷狀態。

    Win32程式沒有這樣的協作多任務,他們必須做好隨時被CPU切換掉的準備。一個真正的Win32程式不應該耗盡CPU時間去等待某些事情的發生。

    Win32API有四個主要的同步對象:(1)Event 事件;(2)Semaphore 訊號量;(3)Mutexes 互斥;(4)Critical Section 臨界區。

    除Critical Setion外,其餘是系統全域對象,並且與不同進程及相同進程中的線程一起工作,這樣同步機制也可以用於進程同步。

    1。事件(Event)

    這是同步對象的一種類型類型,正如其名字含義,在這個中心周圍是一些發生在另一個進程或線程中的特殊活動。當你希望線程暫時掛起時,不會消耗CPU的工作周期。事件類別似我們常用的訊息概念。如果我們剖析訊息的核心,肯定能發現,它就是用事件來實現的。這裡解釋一下事件與訊息的區別:

    事件實際上就是訊息的到達,也就是說一個訊息經過一系列的過程到達了,它就會觸發一個事件處理器,事件處理器就會調用你寫的事件處理函數。而訊息就是發訊息給系統,系統會有訊息佇列。然後系統根據一定的調度會取到等待處理的訊息(當然有可能丟失)來調用訊息相應函數。雖然效果一樣,但是事件系統顯然跟安全,因為不會丟失訊息。

    程式可用CreateEvent或OpenEvent對事件獲得一個控制代碼:

    HANDLE CreateEvent (
                      LPSECURITY_ATTRIBUTES
lpEventAttributes, // SD
                      BOOL bManualReset,                       // reset type
                      BOOL bInitialState,                      // initial state
                      LPCTSTR lpName                           // object name
                    );

 

     HANDLE OpenEvent(
                      DWORD
dwDesiredAccess// access
                      BOOL bInheritHandle,    // inheritance option
                      LPCTSTR lpName          // object name
                    );

    函數參數和傳回值解釋請參考MSDN,以下相同。

 

    然後,該程式再調用WaitForSingleObject,選定事件控制代碼和等待逾時時間。那麼線程就會被掛起,一直到其他線程調用下面API函數,給出事件有關訊號後才再次被啟用。

    BOOL SetEvent(
                  HANDLE
hEvent   // handle to event

                );

 

    BOOL PulseEvent(
                  HANDLE
hEvent   // handle to event object
                );

    比如,當一個線程要使用另一個線程的排序結果時,你或許希望去使用一個事件。比較糟糕的方法是執行這個線程,並在結束時設定全域變數標誌,另一個線程迴圈檢查這個標誌是否已設定,這就浪費很多CPU時間。用事件作同樣的事情則很簡單,排序線程在結束時產生一個事件,其他線程調用WaitForSingleObject。這就使得線程被掛起。當排序線程完成時,調用SetEvent喚醒另一個線程繼續執行。

    除了WaitForSingleObject外,還有WaitForMultipleObjects,允許一個線程被掛起,直到滿足多個Event條件。

    舉例說明。

    音視頻通訊過程中,我們用一個TCP Socket,m_hDataSock接收資料,在沒有資料到達時,接收線程會被掛起,直到有資料到達或者Socket逾時,來進行相應處理,樣本方法如下:

UINT RecvDataThread(LPVOID pPara)
{
     WSAEVENT  =  WSACreateEvent();
     WSAEventSelect(m_hDataSock,m_hEvent,FD_READ | FD_CLOSE);
     while(!m_bThreadEnd)
     {
          DWORD dwWait = WSAWaitForMultipleEvents(1,&m_hEvent,FALSE,18000,FALSE);
          if (WSA_WAIT_TIMEOUT == dwWait)
          {
              //逾時處理
               break;
          }
          if(WAIT_OBJECT_0 == dwWait)
          {
               WSANETWORKEVENTS netEvents;
               if(SOCKET_ERROR == WSAEnumNetworkEvents(m_hDataSock,m_hEvent,&netEvents))
               {
                    continue;
               }
               if((netEvents.lNetworkEvents & FD_READ) && (0 == netEvents.iErrorCode[FD_READ_BIT]))
               {
                    //接收資料
               }
               else if(netEvents.lNetworkEvents & FD_CLOSE)
               {
                      //處理通道關閉

                        break;
               }
          }
     }
     WSACloseEvent(m_hEvent);
     _endthreadex(0);
     return 0;
}

    

    2。訊號量

    當需要限制訪問特殊資源或限制一段代碼到某些線程是,Semaphores非常有用。比如說,一樣資源有十個,當你需要用時,已經被其他十個人佔用了。這樣就必須等待,直到有人不用了,歸還了資源。

    在Win32編程中獲得Semaphores就好像獲得該資源的一次控制。

    為了利用Semaphores,一個線程調用CreateSemaphore去獲得一個HANDLE給Semaphores。也就是將Semaphores與資源綁定,並初始化該Semaphores,並返回該Semaphores的控制代碼。

    函數原型如下:

    HANDLE CreateSemaphore(
                      LPSECURITY_ATTRIBUTES
lpSemaphoreAttributes, // SD
                      LONG lInitialCount,                          // initial count
                      LONG lMaximumCount,                          // maximum count
                      LPCTSTR lpName                               // object name
                      );

    如果Semaphores在其他進程中建立,可以用OpenSemaphore去擷取其控制代碼。

    HANDLE OpenSemaphore(
                      DWORD
dwDesiredAccess// access
                      BOOL bInheritHandle,    // inheritance option
                      LPCTSTR lpName          // object name
                      );

    接下來當然是利用等待函數來阻塞線程。如果這個Semaphore計數大於0,這等待功能只是簡單處理Semaphores的使用數,線程繼續執行,換句話說,如果Semaphores使用數超出最大值,則等待線程被掛起。當然也可以利用ReleaseSemaphore來釋放資源。

    BOOL ReleaseSemaphore(
                  HANDLE
hSemaphore,       // handle to semaphore
                  LONG lReleaseCount,      // count increment amount
                  LPLONG lpPreviousCount   // previous count
                  );

    也就是用訊號量這個對象來管理某個資源的分配與回收。

    

    3。互斥(Mutexes)

    Mutex是“mutual exclusion”的縮寫。希望一次只有一個線程去訪問一個資源或一段代碼時可以使用互斥。使用方法與訊號量類似。建立和釋放Mutex的函數原型如下:

    HANDLE CreateMutex(
                      LPSECURITY_ATTRIBUTES
lpMutexAttributes,  // SD
                      BOOL bInitialOwner,                       // initial owner
                      LPCTSTR lpName                            // object name
                    );

    BOOL ReleaseMutex(
                      HANDLE
hMutex   // handle to mutex
                    );

    可以將使用方法封裝成類,如下:

    class CMutex  
    {
        public:
             CMutex(HANDLE hMutex){m_hMutex = hMutex; WaitForSingleObject(m_hMutex,INFINITE);}
             virtual ~CMutex(){ReleaseMutex(m_hMutex);}
        private:
             HANDLE m_hMutex;
    };

    使用的時候首先聲明一個HANDLE  m_hMutex;調用介面建立Mutex,m_hMutex = CreateMutex(NULL,FALSE,NULL);然後再任何需要互斥的代碼前構造這樣一個類就可以了。比如,CMutex mutex(m_hMutex);

 

    4。臨界區(Critical Sections)

    臨界段相當於一個微型的互斥,只能被同一個進程中的線程使用。臨界區是為了防止多線程同時執行一段代碼。相對其他同步機而言,臨界區相對簡單和易用。一般先聲明一個CRITICAL_SECTION類型的全域變數,然後調用下面三個函數來使用它。

    VOID InitializeCriticalSection(
               LPCRITICAL_SECTION
lpCriticalSection  // critical section
                );

 

    VOID EnterCriticalSection(
              LPCRITICAL_SECTION
lpCriticalSection  // critical section
                );

 

    VOID LeaveCriticalSection(
              LPCRITICAL_SECTION
lpCriticalSection   // critical section
                );

    5。WaitForSingleObject/WaitForMultipleObjects函數

    其實,線程同步,除了上面四種方法外,還可以使用WaitForSingleObject/WaitForMultipleObjects函數。等待的HANDLE可以是線程的控制代碼,檔案的控制代碼等。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.