您的位置:首页 > 其它

音视频(Windows同步机制 )

2012-11-15 13:46 429 查看
音视频包: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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息