音视频包: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