本篇博文讲述WinCE平台下多线程同步的解决方案,下一篇博文讲述进程间共享的方法。
1. 功能:实现同一个进程中多个线程间的同步,避免竞争。
2. 实现:
2.1 平台:WinCE6.0 + VS2005
2.2 思路:进程间切换是程序运行的地址空间的改变;而线程间切换仅仅是执行环境的改变。显然后者效率更高。多线程同步最常用的有4种:1)互斥量、2)事件对象、3)关键代码段、4)信号量。
2.2.1 互斥量
1)创建和清除互斥量对象句柄
HANDLE hMutex; // 全局句柄
void main()
{
hMutex = CreateMutex(NULL,FALSE,NULL); // 创建句柄
ReleaseMutex(hMutex);
…………
CloseHandle(hMutex); // CloseHandle(XX);作用是使内核对象计数减1。
}
CreateMutex的3个参数的解释:
第一个参数,是互斥对象的安全属性,设置NULL为默认的安全属性;
第二个参数,为TRUE表示调用者创建互斥对象,调用线程(创建互斥对象的线程)获得互斥对象的所有权;为FALSE表示调用线程不获得互斥对象的所有权。
第三个参数,表示互斥对象的名字,NULL表示匿名的互斥对象。如果名字为NULL,则互斥量只能在一个进程中使用,如果互斥量的名字是非空,则互斥量可在多个进程间访问。
2)使用互斥量
使用WaitforSingleObject()函数访问互斥量,该函数的作用是使互斥对象内部的计数器的值加1,因此,执行完这段数据处理代码后,要调用ReleaseMutex(hMutex)使互斥量内核对象减1。只有计数器的值变为0时,才能被其他线程访问这个互斥量。
仅在下面两种情况下返回:指定的对象处于有信号状态,或者超时。
代码为:
if(WaitForSingleObject(g_hMutex,INFINITE)==WAIT_OBJECT_0)
......
ReleaseMutex()
// 当线程对共享资源访问结束时,在当前线程中调用这个函数释放对该对象的所有权,也就是让该对象处于已通知状态。然后操作系统将互斥对象的线程ID设置为0,使互斥对象设置为有信号状态。使得其他线程有机会获得对该对象的所有权,从而获得对共享资源的访问。
3)相关细节
在哪个线程使用了互斥对象,就要在哪个线程释放互斥对象。因为互斥对象的ID存储了被调用线程的ID。当存储的线程ID与释放互斥对象的线程ID不一致时,不释放互斥对象。谁拥有互斥对象,谁释放互斥对象。
如果线程结束,则线程会自动检查互斥对象的计数器的值是否为0,如果不为0,则线程会自动将互斥对象的计数器的值复位为0.
2.2.2 事件对象
1)创建和清除事件对象句柄
HANDLE g_hEvent;
void main()
{
g_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL)
}
2)使用事件对象
if(WaitForSingleObject(g_hEvent,INFINITE)==WAIT_OBJECT_0)
......
SetEvent(g_hEvent);
// 当线程对共享资源访问结束时,应该释放该对象的所有权,也就是让该对象处于有信号状态。
3)相关细节
线程在使用事件对象时,事件对象处于无信号状态,直到这个线程时间片到期则让出事件对象的所有权,然后使事件对象处于有信号状态,其他线程就可以使用这个事件对象了。
线程间的同步,最好采用自动重置的事件对象,并设置初始状态为无信号状态。
将信号重置设置为手动重置时,因为当事件对象变成有信号状态时,所有等待线程均可执行。而自动重置的事件对象,当事件对象变成有信号状态时,只有一个等待线程会执行。
使用事件对象时,自动重置的更深层理解:假设线程1使用WaitForSingleObject(g_hEvent,INFINITE);请求到事件对象后,操作系统会将该事件对象设置为无信号状态。为了让本程序能够正常运行,在线程对保护的代码访问完成之后应该立即调用SetEvent函数,将该事件设置为有信号状态,允许其他等待该对象的线程变成可调度状态。
2.2.3 关键代码段
关键代码段的使用方法比较简单,不过多赘述。给出用到的4个函数如下:
InitializeCriticalSection();
DeleteCriticalSection();
EnterCriticalSection();
LeaveCriticalSection();
在WinCE平台下,实现多线程同步的最简单的方式就是关键代码段。
2.2.4 信号量
1)适用范围:用于一个生产者,多个消费者的模式,这样任一时刻就不是一个线程在执行了,而是多个线程可同时执行。
2)类似于互斥量和事件对象,使用了4个函数如下:
HANDLE m_Semphore; // 全局信号量句柄
m_Semphore = CreateSemaphore(NULL,1,1,NULL); // 创建信号量句柄
CloseHandle(m_Semphore); // 在线程结束时,调用此函数关闭句柄
WaitForSingleObject();
ReleaseSemaphore();
3. 总结:
互斥量和关键代码段适用于2个线程相互切换运行的情况。而事件对象,则适用于在一个线程中通过程序产生事件作一些处理,然后让事件对象处于有信号状态,使另一个线程获取该事件对象,然后执行另外一些处理。而且事件对象用于线程同步是最灵活的。信号
量适用于一对多的情况。