音視頻(Windows同步機制 )

音視頻包:http://down.51cto.com/data/507550

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

    對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可以是線程的句柄,文件的句柄等。

對於本博有什麼疑問的朋友可加QQ:992139738

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章