Windows进程同步之信号量内核对象(Semaphore)

信号量内核对象主要包括三个部分:使用计数最大资源计数当前资源计数

  • 使用计数:和其他内核对象一样,用来标识使用该事件对象的不同线程个数;
  • 最大资源计数:表示信号量控制的最大资源的数目;
  • 当前资源计数:表示信号量当前可用的资源数目;

信号量使用规则如下:

  1. 如果当前资源计数大于0,信号量处于触发状态;
  2. 如果当前资源计数等于0,那么信号量处于未触发状态;
  3. 系统绝不会使当前资源计数变为负数;
  4. 系统绝对不会使当前资源计数超过最大资源计数;

在使用信号量对象时,不要把信号量对象使用计数和当前资源计数混为一谈;

下面是使用信号量内核对象的所要使用的函数接口:

1CreateSemaphore()

HANDLE WINAPI CreateSemaphore(
  _In_opt_  LPSECURITY_ATTRIBUTES lpEventAttributes,
  _In_      LONG lInitialCount,
  _In_      LONG lMaximumCount,
  _In_opt_  LPCTSTR lpName
);

lpEventAttributes:事件对象的安全属性,一般置为NULL

lInitialCount:表示一开始可以使用的资源数目,即当前资源计数;

lMaximumCount:信号量对象可以处理的最大资源数量;

lpName:创建有名的信号量对象,用于进程间的共享;

如果该信号量对象已经存在,那么CreateSemaphore会返回该内核对象的句柄,并通过系统返回错误ERROR_ALREADY_EXISTS,通过GetLastError()获得。

2OpenSemaphore()

HANDLE WINAPI OpenSemaphore(
  _In_  DWORD dwDesiredAccess,
  _In_  BOOL bInheritHandle,
  _In_  LPCTSTR lpName
);

dwDesiredAccess:指定想要的访问权限,SEMAPHORE_ALL_ACCESS请求对事件对象的完全访问,SEMAPHORE_MODIFY_STATE修改状态权限,使用ReleaseSemaphore函数需要该权限;

bInheritHandle:是否希望子进程继承信号量对象的句柄,一般设置为false

lpName:要打开的信号量对象的名称;

其他进程中的线程可以通过OpenSemaphoreCreateSemaphore访问已经存在的信号量内核对象。和其他内核对象的访问一样。

3WaitForSingleObject()
DWORD WINAPI WaitForSingleObject(
  _In_  HANDLE hHandle,
  _In_  DWORD dwMilliseconds
);

hHandle:指向内核对象的句柄;

dwMilliseconds:线程最大等待多长时间,直到该对象被触发。经常使用INFINITE,表示阻塞等待。

WaitForSingleObject被称呼为等待函数,是等待内核对象被触发通用的等待函数,被用在所有的内核对象触发等待中。等待函数会检查信号量的当前资源使用计数,如果大于0,表示信号量处于触发状态,那么等待函数会把资源使用计数器减1,并让调用线程继续执行。信号量的最大优势就是在于以原子的方式来执行这些测试和设置操作。如果等于0,表示信号量处于未触发状态,那么系统会让调用线程进入等待状态,直到被唤醒(当前资源计数大于0)。

4ReleaseSemaphore()

BOOL WINAPI ReleaseSemaphore(
  _In_       HANDLE hSemaphore,
  _In_       LONG lReleaseCount,
  _Out_opt_  LPLONG lpPreviousCount
);

hSemaphore:信号量内核对象的句柄;

lReleaseCount:释放自己使用的资源数目,加到信号量的当前资源计数上,通常会传1,当然是根据线程使用的资源数目而定。

lpPreviousCount:返回当前资源计数的原始值,应用程序很少会用到这个值,所以一般置为NULL

当一个线程使用完信号量对象控制的有限资源后,应该调用ReleaseSemaphore,释放使用的资源,使信号量对象的当前资源计数得到恢复。

5CloseHandle()

BOOL WINAPI CloseHandle(
  _In_  HANDLE hObject
);

hObject:指向内核对象的句柄

和其他内核对象一样,无论以什么方式创建内核对象,我们都必须通过调用CloseHandle向系统表明结束使用内核对象。如果传入的句柄有效,系统将获得内核对象数据结构的地址,并将结构中的使用计数减1,如果使用计数0,就会将内核对象销毁,从内存空间中擦除。

下面是信号量部分代码测试:

#include <iostream>
#include <fstream>
#include <windows.h>
#include <process.h>

using namespace  std;

void semaphoreTest(void *ptr)
{
    int flag = *(int *)ptr;

    HANDLE semaphore = CreateSemaphore(NULL, 2, 2, (LPCWSTR)"streamSemaphore");

    WaitForSingleObject(semaphore, INFINITE);

    ofstream fileStream("c:/test.txt", ios_base::app);

    for (int i = 0; i < 5; ++i)
    {
        Sleep(1000);
        fileStream<<flag;
        fileStream<<' '<<flush;

        //cout<<flag<<' ';
    }

    fileStream.close();

    ReleaseSemaphore(semaphore, 1, NULL);
    CloseHandle(semaphore);
}


int main()
{
    int flag[] = {1, 2, 3};

    for (int i = 0; i < 3; ++i)
    {
        _beginthread(semaphoreTest, 0, &flag[i]);
    }
    
    Sleep(INFINITE);
        
}

代码在主线程中依次启动三个线程,每个线程对同一个文件进行写操作,但同一时间最多只能有两个线程对这个文件进程操作,所以执行结果如下:

2 1 2 1 2 1 2 1 2 1 3 3 3 3 3


Jun 19, 2013 AM 01:29 @dorm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值