CreateSemaphore和ReleaseSemaphore函数(转)

来源:互联网 发布:网页音频下载软件 编辑:程序博客网 时间:2024/06/07 11:56

在开发软件的过程中,多线程的程序往往需要实现相互通讯,比如几个线程添加一个消息到队列里,而另一个线程在睡眠时,就需要唤醒那个线程来处理事情。在这其中,就需要使用到信号量来进行同步。CreateSemaphore是创建信号量,ReleaseSemaphore是增加信号量。

函数CreateSemaphore和ReleaseSemaphore声明如下:
WINBASEAPI
__out
HANDLE
WINAPI
CreateSemaphoreA(
    __in_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
    __in     LONG lInitialCount,
    __in     LONG lMaximumCount,
    __in_opt LPCSTR lpName
    );
WINBASEAPI
__out
HANDLE
WINAPI
CreateSemaphoreW(
    __in_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
    __in     LONG lInitialCount,
    __in     LONG lMaximumCount,
    __in_opt LPCWSTR lpName
    );
#ifdef UNICODE
#define CreateSemaphore CreateSemaphoreW
#else
#define CreateSemaphore CreateSemaphoreA
#endif // !UNICODE

lpSemaphoreAttributes是信号量的安全属性。
lInitialCount是初始化的信号量。
lMaximumCount是允许信号量增加到最大值。
lpName是信号量的名称。

WINAPI
ReleaseSemaphore(
    __in      HANDLE hSemaphore,
    __in      LONG lReleaseCount,
    __out_opt LPLONG lpPreviousCount
    );

hSemaphore是要增加的信号量句柄。
lReleaseCount是增加的计数。
lpPreviousCount是增加前的数值返回。

调用函数的例子如下:
#001 //线程运行函数。
#002 //在这里可以使用类里的成员,也可以让派生类实现更强大的功能。
#003 //蔡军生 2007/10/10 QQ:9073204 深圳
#004 DWORD CThreadSemaphore::Run(void)
#005 {
#006 //输出到调试窗口。
#007 ::OutputDebugString(_T("Run()线程函数运行\r\n"));      
#008 
#009 //
#010 const LONG cMax = 10;
#011   m_hSemaphore = CreateSemaphore( 
#012        NULL,   // 缺省的安全属性。
#013        0,   // 初始化为0个信号量。
#014        cMax,   // 最大为10个信号量。
#015        NULL); // 不命名。
#016 
#017 if (m_hSemaphore == NULL) 
#018 {
#019         return -1;
#020 }
#021 
#022 //
#023 const int nMaxObjs = 2;
#024 HANDLE hWaitObjects[nMaxObjs] = {m_hEventExit,m_hSemaphore};
#025 
#026 //线程循环。
#027 for (;;)
#028 {
#029         DWORD dwRet = WaitForMultipleObjects(nMaxObjs,hWaitObjects,FALSE,INFINITE);
#030         if (dwRet == WAIT_TIMEOUT)
#031         {
#032               //可以继续运行。                 
#033               TCHAR chTemp[128];
#034               wsprintf(chTemp,_T("CThreadSemaphore::Run() ThreadID=%d\r\n"),m_dwThreadID);
#035               ::OutputDebugString(chTemp);
#036 
#037               //目前没有做什么事情,就让线程释放一下CPU。
#038               Sleep(10);
#039         }
#040         else if (dwRet == WAIT_OBJECT_0)
#041         {
#042               //退出线程。
#043               ::OutputDebugString(_T("Run() 退出线程\r\n"));
#044               break;
#045         }
#046         else if (dwRet == WAIT_OBJECT_0+1)
#047         {
#048               //可以继续运行。                 
#049               TCHAR chTemp[128];
#050               wsprintf(chTemp,_T("CThreadSemaphore::Run() Semaphore,ThreadID=%d\r\n"),m_dwThreadID);
#051               ::OutputDebugString(chTemp);
#052 
#053               //
#054 
#055         }
#056         else if (dwRet == WAIT_ABANDONED)
#057         {
#058               //出错。
#059               ::OutputDebugString(_T("Run() 线程出错\r\n"));
#060               return -1;
#061         }
#062 }
#063 
#064 //
#065 if (m_hSemaphore)
#066 {
#067         CloseHandle(m_hSemaphore);
#068          m_hSemaphore = NULL;
#069 }
#070 
#071 return 0;
#072 }
#073 
第11行就是创建信号量。
第29行等信号量事件和退出事件。


#001 
#002 //
#003 //增加信号量
#004 //蔡军生 2007/10/10 QQ:9073204 深圳
#005 //
#006 void IncSemaphore(void)
#007 {
#008         if (m_hSemaphore)
#009         {
#010              if (!ReleaseSemaphore( 
#011                    m_hSemaphore, // 要增加的信号量。
#012                   1,           // 增加1.
#013                   NULL) )      // 不想返回前一次信号量。
#014               {
#015 
#016               }
#017         }
#018 }
#019

Semaphore是另一个同步问题机制,不论是Event或Mutex,其他Process在执WaitForSingleObject
时,就看当时的物件是Signal或UnSignal而决定是否等待,而Semaphore也相同,但是它
要变成Signal /UnSignal的状态,却有些不同,它是提供一个计数值,它允许在这个计数
值之内,任何执行到WaitForSingleObject的Thread都不会停下来,而且每执行
WaitForSingleObject一次,计数值就减一,当计数值变成0时,该Semaphore才会处於
UnSignal的状态,而某个Thread ReleaseSemaphore时,便会将计数值增加,以便其他的
Thread或本身可得Signal的讯号,而使WaitForSingleObject停止等待。

例如说,该电脑只有两个 COM PORT,所以只允许两个计数值同时使用COM PORT,因此,

    hSema = CreateSemaphore(ByVal 0&, 2, 2, "MySema")

第2个叁数表示:刚开始的时候,有多少个COM PORT可使用
第3个叁数表示:最多有多少个COM PORT可使用
第4个叁数:Semaphore的名称,只要名称相同,则传回的handle(hSema)会指向相同的
Semaphore物件。因此,要使用相同的名称来Create Semaphore才能达共用
一个Semaphore的效果。
而使用WaitForSingleObject来Check看看是否还有剩下的COM Port可使用,如果还有剩
(计数值 > 0),则没有等待而可执行下一行指令,同时,计数值减1。若有第三个要求
COM PORT的使用,那它就得等待,直到有Thread执行

    ReleaseSemaphore(hSema, 1, count)

第2个叁数表示:Release多少个COM PORT出来,一般来说都是1,表示一个ReleaseSemaphore
       会将计数器的值加一,但是您也可以指定 > 1的值,代表一口气增加计数器
       的值( + n , n > 1)。例如,您的程式一口气使用了两个COM PORT,并假设
       您於程式中有使用WaitForSingleObject两次,程式最後,使用
       ReleaseSemaphore(hSema, 2, count)而不必
       ReleaseSemaphore(hSema, 1, count)执行两次。
第3个叁数表示:ReleaseSemaphore执行之前计数器原来的值。

Semaphore和Event有个地方相同,那就是没有Owner的观念,即Thread A 所Create出的
Semaphore物件,於Thread B中执行ReleaseSemaphore时,依然会增加计数器的值

'以下程式需两个Command BUTTON 一个ListBox,并产生两个执行个体来做Private Const INFINITE = &HFFFFPrivate Declare Function CreateSemaphore Lib "kernel32" Alias "CreateSemaphoreA" (lpSemaphoreAttributes As Any, ByVal lInitialCount As Long, ByVal lMaximumCount As Long, ByVal lpName As String) As Long ' modified by KJPrivate Declare Function OpenSemaphore Lib "kernel32" Alias "OpenSemaphoreA" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal lpName As String) As LongPrivate Declare Function ReleaseSemaphore Lib "kernel32" (ByVal hSemaphore As Long, ByVal lReleaseCount As Long, lpPreviousCount As Long) As LongPrivate Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As LongPrivate Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As LongPrivate hSema As LongPrivate Sub Command1_Click() Call WaitForSingleObject(hSema, INFINITE) '一直等到有可用的COM PORT才结束 List1.AddItem "可用COM PORT 减一" List1.ListIndex = List1.NewIndex '在此处理Com Port的程式End SubPrivate Sub Command2_Click() Dim count As Long '在此处理com port Close的动作 If ReleaseSemaphore(hSema, 1, count) Then List1.AddItem "剩下可用COM PORT等於:" & count + 1 Else List1.AddItem "Semaphore Release 无效" End If List1.ListIndex = List1.NewIndexEnd SubPrivate Sub Form_Load() hSema = CreateSemaphore(ByVal 0&, 2, 2, "MySema")End SubPrivate Sub Form_Unload(Cancel As Integer) Call CloseHandle(hSema)End Sub