Windows线程同步——互斥量对象

1. 概述

当两个或更多线程需要同时访问一个共享资源时,为了保证程序的正常运行,需要保证同一个资源在同一时刻只能有一个线程去访问它。Mutex 是同步基元,它只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。

互斥量与关键段(线程同步方式)的行为完全相同,当互斥量是内核对象,而关键段是用户模式下的的同步对象。互斥量对象包含: 一个线程 ID ,使用计数和递归计数 。线程 ID 表示当前占用该互斥量的线程 ID ,递归计数表示该线程占用互斥量的次数,使用计数表示使用互斥量对象的不同线程的个数。

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

2. 互斥量相关API

2.1 CreateMutex()函数

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 ()获得。

2.2 OpenMutex()函数

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 来实现打开功能。

2.3 WaitForSingleObject()函数

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

参数说明

hHandle :指向内核对象的句柄;
dwMilliseconds:线程最大等待多长时间,直到该对象被触发。经常使用INFINITE ,表示阻塞等待。

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

2.4 ReleaseMutex() 函数

BOOL WINAPI ReleaseMutex(
  _In_ HANDLE hMutex
);

参数说明

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

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

3. 示例代码

DWORD WINAPI my_thread1(LPVOID m_pParameter);	//用户线程1
DWORD WINAPI my_thread2(LPVOID m_pParameter);	//用户线程2
UINT count = 0;									//全局的计数函数
HANDLE m_hMutex;								//定义一个互斥量句柄

int _tmain(int argc, _TCHAR* argv[])
{
	system("color f0");
	int count_size(50);
	m_hMutex = ::CreateMutex(NULL, false, NULL);		//初始化互斥量
	HANDLE m_h1 = CreateThread(NULL, NULL, my_thread1, &count_size, NULL, NULL);	//用户线程1
	HANDLE m_h2 = CreateThread(NULL, NULL, my_thread2, &count_size, NULL, NULL);	//用户线程2
	::WaitForSingleObject(m_h1, INFINITE);	//等待子线程结束
	::WaitForSingleObject(m_h2, INFINITE);	//等待子线程结束
	::CloseHandle(m_hMutex);			//关闭句柄
	::CloseHandle(m_h1);
	::CloseHandle(m_h2);
	system("pause");
	return 0;
}

//用户线程1
DWORD WINAPI my_thread1(LPVOID m_pParameter)
{
	UINT* my_count = (UINT*)m_pParameter;
	cout << "thread1" << endl;
	for (int i = 0; i < *my_count; ++i)
	{
		::WaitForSingleObject(m_hMutex, INFINITE);	//等待互斥量
		count = i;
		cout << count << "\t";
		::ReleaseMutex(&m_hMutex);					//释放互斥量
	}
	return 0;
}

//用户线程2
DWORD WINAPI my_thread2(LPVOID m_pParameter)
{
	UINT* my_count = (UINT*)m_pParameter;
	cout << "thread2" << endl;
	for (int i = 0; i < *my_count; ++i)
	{
		::WaitForSingleObject(m_hMutex, INFINITE);	//等待互斥量
		count = i;
		cout << count << "\t";
		::ReleaseMutex(&m_hMutex);					//释放互斥量
	}
	return 0;
}




你可能感兴趣的:([12],Windows进程与线程,Windows进程与线程)