Windows进程同步之互斥量内核对象(Mutex)

我们知道进程间的同步操作都是要借助内核来完成的,和同一个进程中的线程同步只需要在用户模式下是有很大差别的,当然,对于进程安全的,对于线程肯定也是安全的,但在用户模式下的线程同步所需消耗的代价相对于通过内核完成的同步是很小的。所以不要利用进程同步的方式来进行同一进程中线程的同步。

这里先讨论进程同步的方式之一:互斥量(Mutex)。

互斥量内核对象能够确保一个进程独占对一个资源的访问。互斥量与关键段(线程同步方式)的行为完全相同,当互斥量是内核对象,而关键段是用户模式下的的同步对象。

互斥量对象包含:一个线程ID,使用计数和递归计数。线程ID表示当前占用该互斥量的线程ID,递归计数表示该线程占用互斥量的次数,使用计数表示使用互斥量对象的不同线程的个数。

互斥量对象有许多用途,它是使用最为频繁的内核对象之一,一般用来对多个进程访问同一块内存进行同步。

互斥量的使用如下:

1CreateMutex()

HANDLE WINAPI CreateMutex(
  _In_opt_  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  _In_      BOOL bInitialOwner,
  _In_opt_  LPCTSTR lpName
);

lpMutexAttributes:互斥量安全访问属性,一般置为NULL

bInitialOwner:控制互斥量的初始状态,一般设置为false,是互斥量的线程ID和递归计数都设为0,表示互斥量不被任何进程占用,处于触发状态,其他进程可以进行调用等待函数,获得该互斥量对象,获得对同步资源的占用。如果初始值设为true,互斥量的线程ID设置为当前线程,递归计数设为1,表示当前线程占用互斥量对象,拥有对同步资源的独占,互斥量处于未触发状态。

lpName:用于创建有名的内核对象,即用来创建跨进程边界的内核对象。

CreateMutex用于创建名为lpName的互斥量内核对象,并返回指向该内核对象的句柄。如果该内核对象已经存在,那么CreateMutex会返回该内核对象的句柄,并通过系统返回错误ERROR_ALREADY_EXISTS,通过GetLastError()获得。

2OpenMutex()

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

dwDesiredAccess:对互斥量对象访问权限的设置,MUTEX_ALL_ACCESS请求对互斥体的完全访问,MUTEX_MODIFY_STATE允许使用ReleaseMutex函数,SYNCHRONIZE允许互斥体对象同步使用;

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

lpName:要打开的互斥量对象的名称;

OpenMutex用于打开已经存在的互斥量对象,若打开成功,则返回指向该内核对象的句柄,否则返回NULL。可以使用CreateMutex来实现打开功能。

3WaitForSingleObject()

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

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

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

WaitForSingleObject被称呼为等待函数,是等待内核对象被触发通用的等待函数,被用在所有的内核对象触发等待中。等待函数在等待互斥量内核对象时,会判断互斥量的线程ID是否为0,如果为非0,表示互斥量处于未触发状态,等待函数会被阻塞。当另外一个线程将互斥量释放,使其线程ID0时,系统会唤醒阻塞的等待函数,把互斥量的线程ID设置为它的线程ID,使其成为可调度状态

还有一个WaitForMultipleObject函数,用于等待多个内核对象被触发。

4ReleaseMutex()

BOOL WINAPI ReleaseMutex(
  _In_  HANDLE hMutex
);

hMutex:互斥量内核对象的句柄;

当一个线程访问完通过互斥量对象获得的独占资源后,应该调用ReleaseMutex,使互斥量恢复为未触发状态。即设置互斥量对象的线程ID和递归计数为0,当递归计数大于1时,还有进行对应多次的ReleaseMutex

5CloseHandle()
BOOL WINAPI CloseHandle(
  _In_  HANDLE hObject
);

hObject:指向内核对象的句柄

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

例如下面的实例,用来同步两个进程,对同一个文件的操作:

//test1.cpp
int main()
{
    ofstream fileStream1("c:/test.txt", ios_base::app);

    for (int i = 0, j = 1; i < 10; ++i)
    {
        Sleep(1000);
        fileStream1<<j;
        fileStream1<<' '<<flush;
    }
}

//test2.cpp
int main()
{
    ofstream fileStream2("c:/test.txt", ios_base::app);

    for (int i = 0, j = 2; i < 10; ++i)
    {
        Sleep(1000);
        fileStream2<<j;
        fileStream2<<' '<<flush;
    }
}

上面两个程序同时执行时,这里没有采取同步措施,执行的结果可想而知,输出结果会相互交错,"c:/test.txt"内容如下:

2 2 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 1 1 

下面代码是采用内核对象进行同步操作:

//test1.cpp
int main()
{   
    HANDLE stream1Mutex = CreateMutex(NULL, false, (LPCWSTR)"streamMutex");
  WaitForSingleObject(stream1Mutex, INFINITE);
  
    ofstream fileStream1("c:/test.txt", ios_base::app);
    for (int i = 0, j = 1; i < 10; ++i)
    {
        Sleep(1000);
        fileStream1<<j;
        fileStream1<<' '<<flush;
  }
  
    ReleaseMutex(stream1Mutex);
    CloseHandle(stream1Mutex);
}

//test2.cpp
int main()
{
    HANDLE stream2Mutex = CreateMutex(NULL, false, (LPCWSTR)"streamMutex");
    WaitForSingleObject(stream2Mutex, INFINITE);

    ofstream fileStream2("c:/test.txt", ios_base::app);
    for (int i = 0, j = 2; i < 10; ++i)
    {
        Sleep(1000);
        fileStream2<<j;
        fileStream2<<' '<<flush;
    }

   ReleaseMutex(stream2Mutex);
    CloseHandle(stream2Mutex);
}	

测试结果如下:

1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 

Jun 12, 2013 @dorm


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值