音视频(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

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