C++信号量Semaphore和MFC中的CSemaphore类使用
扫描二维码
随时随地手机看文章
1 BOOL ReleaseSemaphore( 2 HANDLE hSemaphore, // 信号量句柄 3 LONG lReleaseCount, // 计数递增数量 4 LPLONG lpPreviousCount // 先前计数 5 ); 6 7
C++信号量Semaphore和MFC中的CSemaphore类使用
信号量(Semaphore )内核对象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最 大线程数目。
在用CreateSemaphore () 创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最 大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1 ,只要当前可用资源计数是大于0 的,就可以发出信号量信号。但是当前可用计数减小 到0 时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离 开的同时通过ReleaseSemaphore ()函数将当前可用资源计数加1 。在任何时候当前可用资源计数决不可能大于最大资源计数。
信号量是通过计数来对线程访问资源进行控制的,而实际上信号量确实也被称作Dijkstra 计数器。
使用信号量内核对象进行线程同步主要会用到CreateSemaphore、OpenSemaphore、 ReleaseSemaphore、WaitForSingleObject和WaitForMultipleObjects等函数。CreateSemaphore ()用来创建一个信号量内核对象,其函数原型为:
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指针 LONG lInitialCount, // 初始计数 LONG lMaximumCount, // 最大计数 LPCTSTR lpName // 对象名指针 );
参数lMaximumCount 是一个有符号32 位值,定义了允许的最大资源计数,最大取值不能超过4294967295 。lpName 参数可以为创建的 信号量定义一个名字,由于其创建的是一个内核对象,因此在其他进程中可以通过该名字而得到此信号量。OpenSemaphore ()函数即可用来根据信号 量名打开在其他进程中创建的信号量,函数原型如下:
1 HANDLE OpenSemaphore( 2 DWORD dwDesiredAccess, // 访问标志 3 BOOL bInheritHandle, // 继承标志 4 LPCTSTR lpName // 信号量名 5 );
在线程离开对共享资源的处理时,必须通过ReleaseSemaphore ()来增加当前可用资源计数。否则将会出现当前正在处理共享资源的实际线程数并 没有达到要限制的数值,而其他线程却因为当前可用资源计数为0 而仍无法进入的情况。ReleaseSemaphore ()的函数原型为:
该函数将lReleaseCount 中的值添加给信号量的当前资源计数,一般将lReleaseCount 设置为1 ,如果需要也可以设置其他的值。 WaitForSingleObject ()和WaitForMultipleObjects ()主要用在试图进入共享资源的线程函数入口处,主要用来判 断信号量的当前可用资源计数是否允许本线程的进入。只有在当前可用资源计数值大于0 时,被监视的信号量内核对象才会得到通知。
信号量的使用特点使其更适用于对Socket (套接字)程序中线程的同步。例如,网络上的HTTP 服务器要对同一时间内访问同一页面的用户数加以限制,这 时可以为每一个用户对服务器的页面请求设置一个线程,而页面则是待保护的共享资源,通过使用信号量对线程的同步作用可以确保在任一时刻无论有多少用户对某 一页面进行访问,只有不大于设定的最大用户数目的线程能够进行访问,而其他的访问企图则被挂起,只有在有用户退出对此页面的访问后才有可能进入。下面给出 的示例代码即展示了类似的处理过程:
#include#include#includeusing namespace std; HANDLE hSemaphore; UINT __stdcall Add(LPVOID lParam) { WaitForSingleObject(hSemaphore, INFINITE); for (int i = 0; i<10;i++ ) { arr[i]=i;//0-9 } for (int i = 0;i < 10; i++) { cout<<arr[i]<<" "; } cout<<endl; ReleaseSemaphore(hSemaphore, 1, NULL); //信号量加1。如果不释放。因为当前可用为1个,且被占用了,那么Add2不会执行 return 1; } UINT __stdcall Add2(LPVOID lParam) { WaitForSingleObject(hSemaphore, INFINITE); for (int i = 0; i<100 ;i++) { arr [i] = i+100;//10`1 } for (int i = 0;i < 10; i++) { cout<<arr[i]<<" "; } cout<<endl; ReleaseSemaphore(hSemaphore, 1, NULL); return 1; } int main() { hSemaphore =CreateSemaphore(NULL,1,2,""); //创建无名信号量,初始2个,可用1个 hUp=(HANDLE)_beginthreadex(NULL, 0, Add, NULL, NULL, 0); hUp=(HANDLE)_beginthreadex(NULL, 0, Add2, NULL, NULL, 0); Sleep(5000); }
在MFC 中,通过CSemaphore 类对信号量作了表述。该类只具有一个构造函数,可以构造一个信号量对象,并对初始资源计数、最大资源计数、对象名和安全属性等进行初始化,其原型如下:
CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );
在构造了CSemaphore 类对象后,任何一个访问受保护共享资源的线程都必须通过CSemaphore 从父类CSyncObject 类继承得到的 Lock ()和UnLock ()成员函数来访问或释放CSemaphore 对象。与前面介绍的几种通过MFC 类保持线程同步的方法类似,通过 CSemaphore 类也可以将前面的线程同步代码进行改写,这两种使用信号量的线程同步方法无论是在实现原理上还是从实现结果上都是完全一致的。下面给出经MFC 改写后的信号量线程同步代码:
CSemaphore g_clsSemaphore(1, 2);
MFC中不用WaitForSingleObject。
g_clsSemaphore.Lock计数器减1,g_clsSemaphore.Unlock计数器加1