mutex

时间:2024-11-12 17:41:34编辑:小早

windows下的同步机制有哪些

在多线程程序设计中,不可避免地面临着同步问题。在Win32中,有以下四种同步机制。

1、临界区 - Critical Section
(1) 说明
多线程程序中,有些代码是共享资源,需将这些代码作为临界区。如果有多个线程试图同时访问临界区,那么在一个线程进入后,其他线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占。
临界区的同步速度很快;不是内核对象,因而不能跨进程同步;不能指定阻塞时的等待时间(只能无限等待下去)。
(2) 有关函数
操作临界区要涉及的API函数有:
InitializeCriticalSection()
EnterCriticalSection()
LeaveCriticalSection()
DeleteCriticalSection()
这四个函数的形参都是一个指向CRITICAL_SECTION结构体的指针,因而必须先定义一个CRITICAL_SECTION类型的变量。
InitializeCriticalSection()的作用是初始化一个临界资源对象;EnterCriticalSection()的作用是查看CRITICAL_SECTION结构成员变量的值,判断是否有线程访问临界区的资源。如果没有,则更新CRITICAL_SECTION结构成员变量的值,并将当前的线程赋予资源访问权;如果有线程正在访问临界区的资源,则该函数将线程置为等待状态;LeaveCriticalSection()释放临界区资源的所有权,使其他等待临界区资源的线程能够有机会获得临界区资源的所有权。
(3) 应用
#ifndef _ZCZ_WIN32_TOOLS_CCRITICALSECTION_H_
#define _ZCZ_WIN32_TOOLS_CCRITICALSECTION_H_
#include
namespace zcz_win32_tools
{
class CCriticalSection
{
public:
CCriticalSection();
~CCriticalSection();
public:
void EnterCriticalSection();
void LeaveCriticalSection();
private:
CRITICAL_SECTION m_ObjCriticalSection;
};
class CCriticalSectionOwner
{
public:
CCriticalSectionOwner(CCriticalSection &);
~CCriticalSectionOwner();
private:
CCriticalSection &m_refCCriticalSection;
};
}
#endif

#include "./CCriticalSection.h"
namespace zcz_win32_tools
{
CCriticalSection::CCriticalSection()
{
::InitializeCriticalSection(&m_ObjCriticalSection);
}

CCriticalSection::~CCriticalSection()
{
::DeleteCriticalSection(&m_ObjCriticalSection);
}

void CCriticalSection::EnterCriticalSection()
{
::EnterCriticalSection(&m_ObjCriticalSection);
}

void CCriticalSection::LeaveCriticalSection()
{
::LeaveCriticalSection(&m_ObjCriticalSection);
}

CCriticalSectionOwner::CCriticalSectionOwner(CCriticalSection &ObjCCriticalCestion)
:m_refCCriticalSection(ObjCCriticalCestion)
{
m_refCCriticalSection.EnterCriticalSection();
}

CCriticalSectionOwner::~CCriticalSectionOwner()
{
m_refCCriticalSection.LeaveCriticalSection();
}
}

2、互斥量 - Mutex
(1) 说明
互斥对象的作用是保证每次只能有一个线程获得互斥对象而得以继续执行。互斥对象主要包含使用数量、线程ID和递归计数器等信息。其中,线程ID表示当前拥有互斥对象的线程号,递归计数器表示线程拥有互斥对象的次数。
互斥对象是是Windows的内核对象,可跨进程互斥,并且能指定阻塞时的等待时间。
(2) 有关函数
使用互斥对象要涉及的API函数主要有:
CreateMutex() // 创建互斥对象
ReleaseMutex() // 释放互斥对象
OpenMutex() // 跨进程时使用
WaitForSingleObject() // 等待指定时间
使用互斥编程的一般方法是:
void UpdateResource
{
WaitForSingleObject(hMutex,...);
// do something...
ReleaseMutex(hMutex);
}
(3) 应用
#include
#include
using namespace std;
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int tickets=100;
HANDLE hMutex=CreateMutex(NULL,FALSE,NULL);
void main()
{
HANDLE hThread1,hThread2;
hThread1=::CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
hThread2=::CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
::CloseHandle(hThread1);
::CloseHandle(hThread2);
::Sleep(INFINITE);
}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hMutex,INFINITE);
if (tickets>0)
{
cout<<"t1: "<<tickets--<<endl;
}
else
{
break;
}
ReleaseMutex(hMutex);
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hMutex,INFINITE);
if (tickets>0)
{
cout<<"t2: "<<tickets--<<endl;
}
else
{
break;
}
ReleaseMutex(hMutex);
}
return 0;
}

3、事件 - Event
(1)说明
事件是内核对象,具有“激发状态”和“未激发状态”两种状态。事件主要分为两类:
人工重置事件:用程序手动设置。
自动重置事件:一旦事件发生并被处理后,自动恢复到没有时间状态。
(2)有关函数
使用使用事件对象要涉及的API函数主要有:
CreateEvent() // 创建事件对象
SetEvent() // 设置事件对象
ResetEvent() // 设置事件对象
PulseEvent() // 设置事件对象
OpenEvent() // 跨进程时使用
WaitforSingleEvent() // 等待
WaitForMultipleObjects()// 等待
(3)应用
#ifndef _ZCZ_WIN32_TOOLS_CEVENT_H_
#define _ZCZ_WIN32_TOOLS_CEVENT_H_
#include
namespace zcz_win32_tools
{
class CEvent
{
public:
CEvent();
~CEvent();
public:
BOOL SetEvent();
BOOL ResetEvent();
BOOL WaitInfinite();
private:
HANDLE m_hEvent;
};
}
#endif
#include "./CEvent.h"
namespace zcz_win32_tools
{
CEvent::CEvent():m_hEvent(NULL)
{
m_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}

CEvent::~CEvent()
{
}

BOOL CEvent::SetEvent()
{
if (NULL == m_hEvent || 0 == ::SetEvent(m_hEvent))
{
return FALSE;
}
return TRUE;
}

BOOL CEvent::ResetEvent()
{
if (NULL == m_hEvent || 0 == ::ResetEvent(m_hEvent) )
{
return FALSE;
}
return TRUE;
}

BOOL CEvent::WaitInfinite()
{
if ( WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEvent, INFINITE) )
{
return TRUE;
}
return FALSE;
}
}

4、信号量 - Semaphore
(1)说明
信号量允许多个线程在同一时刻访问统一资源,但是限制了在同一时刻访问共享资源的最大线程数。
信号量是内核对象,允许跨进程使用。
(2)有关函数
使用信号量要涉及的API函数主要有:
CreateSemaphore() // 创建信号量
ReleaseSemaphore() // 释放信号量
OpenSemaphore() // 跨进程使用
在用 CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过 ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。


windows怎么实现线程间的通信

Windows线程间通信
.
1.概述
如果一个进程中的所有线程都不需要相互传递数据就可以顺利完成,那么程序运行的性能自然是最好的,但是实际上,很少有现成能够在所有的时间都独立的进行操作,通常在以下两种情况下,线程之间需要进行通信。
a) 多个线程都对共享资源资源进行访问,但不希望共享资源被破坏。
b) 一个线程完成了任务,要通知其他的线程。
情况a)属于互斥问题,情况b)属于同步问题。通常的解决方法如下:
2.解决方法
a) 互锁函数
互锁函数是windows提供的一个函数族,它可以实现对共享变量的简单原子访问,所谓的原子访问就是说当当前线程正在访问资源时,可以保证其他的线程没有同时访问这个资源。但是它只能完成以原子操作方式修改单个值,作用域很小。
Eg:
Long g = 0;
DWORD _stdcall TF1(PVOID p){
g++;
Return 0;
}
DWORD _stdcall TF2(PVOID p){
g++;
Return 0;
}
在上面的例子中,不能保证运行结束时g的值为2,因为在自加的过程中,线程可能会被另一个线程打断,这时,两个线程可能对同一个变量进行了操作,出现了错误,这是使用互锁函数改为即可保证在一个线程进行g++时,另一个线程不会将其打断。
Long g = 0;
DWORD _stdcall TF1(PVOID p){
InterlockedExchangeAdd(&g, 1);
Return 0;
}
DWORD _stdcall TF2(PVOID p){
InterlockedExchangeAdd(&g, 1);
Return 0;
}
类似的函数还有
LONG InterlockedExchange(PLONG plTarget, LONG lValue);
PVOID InterlockedCompareExchange(PLONG plDestination, LONG lExchange, LONG lComparand);
b) 临界段
临界段也叫做关键代码段,它是一小段代码,通过设置临界区域,能够以原子操作的方式使用资源。具有相同临界资源的临界段只能允许一个线程执行它,其他要进入该段的线程将被挂起,直到前面的线程释放临界资源。
Win32 API中临界段的设置:
首先定义一个全局临界对象,类型为CRITICAL_SECTION
CRITICAL_SECTION cs;
然后调用函数对其初始化:
InitializeCriticalSection(&cs);
这样就创建了一个名为cs的临界段对象了,然后对于可能会发生冲突的线程代码段使用同一个临界对象进行处理即可,进入临界段的代码为:
EnterCriticalSection(&cs);
此时,线程被认为拥有临界段对象,没有两个线程可以同时拥有相同的临界对象,因此,如果一个线程进入了临界段,那么下一个使用相同临界段对象调用EnterCriticalSection的线程将被挂起。离开临界段的函数为:
LeaveCriticalSeciton(&cs);
此时,释放临界对象的所有权,删除临界对象的函数为:
DeleteCriticalSection(&cs);
c) 使用内核对象进行线程通信
互锁函数和临界段都是属于用户态的通信,好处是速度很快,但是对许多应用程序而言是不足的,而使用内核对象进行通信速度较慢,其他的性能较好。使用内核对象进行线程通信的机理是:很多内核对象存在一个属性,用来表示该内核对象是已通知状态还是未通知状态,然后通常使用WaitForSingleObject或WaitForMultipleObjects来等待特定内核对象的已通知状态。

事件对象

事件内核对象通常和WaitForSingleObject等联合使用,事件对象主要用于标志一个操作是否已经完成,其函数为:
HANDLE CreateEvent(
PSECURITY_ATTRIBUTES psa,
BOOL fManualReset,
BOOL fInitialState,
PCTSTR pszName);
第一个参数为安全属性,第二个参数用于设置创建一个人工重置事件(ture)还是自动重置事件,两者的区别在于:如果设置成人工重置事件,则需要使用SetEvent和ResetEvent函数来将事件设置成已通知事件和未通知事件,当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度的;如果设置成自动重置事件,当使用SetEvent设置事件的通知状态时,在等待事件的线程中,只有一个线程被回复(哪个不确定),之后系统自动将事件设为未通知状态。第三个参数用于设置事件初始状态,第四个参数用于设置事件的名字。

互斥对象

互斥量是一种内核对象,它能够确保线程拥有对单个资源的互斥访问权。它与临界段相同,但是互斥量属于内核对象,临界段属于用户对象,这意味着互斥要比临界段慢,但是不同进程中的多个线程能够访问单个互斥量。互斥量不同于所有其他的内核对象,互斥量中有一个线程ID,用于标志该互斥量属于哪个线程(即不论在哪个线程中创建了没有归属的互斥量,只要在某个线程中将互斥量变为未通知事件,那么这个线程就拥有这个互斥量,如果在该线程中释放了这个互斥量,那么该互斥量又变成游离状态,没有所属的线程,然后重复上面的过程),因此互斥量有一个“线程所有权”的概念,因此如果调用ReleaseMutex的线程不拥有互斥量,那么该函数不进行任何操作。
Win32 API互斥量函数:
创建一个互斥量:
HANDLE CreateMutex(
PSECURITY_ATTRIBUTES psa,
BOOL fInitialOwner,
PCTSTR pszName);
第一个参数安全属性,最后一个参数为互斥量的名字,第二个参数:
A) 如果设为FALSE,则表示没有线程拥有该互斥量,线程ID为0,处于已通知 状态。
B) 如果设为TURE,则表示当前线程拥有该互斥,线程ID为当前线程的ID,处 于未通知状态。
当某个拥有互斥的线程不想再拥有该互斥的时候,则调用函数:
ReleaseMutex(HANDLE hMutex);
来释放该线程对该互斥的占有,此后该互斥量又变成游离的状态,直到再出现一个 线程拥有它。

信号量

信号量也是一种内核对象,用于对资源进行计数。信号量的使用规则是:如果当前资源数量大于0,那么等待信号量的线程可以获得一个资源并继续执行,信号量的当前资源数将减1;如果当前资源数为0,那么等待信号量的线程将处于等待状态,直到有线程释放信号量,使当前资源数大于0,当前资源数不会超过最大资源数量值,也不会小于0。
Win32用于创建和释放信号量的API函数
HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTE psa,
LONG lInitialCount,
LONG lMaximumCount,
PCTSTR pszName);
BOOL ReleaseSemaphore(
HANDLE hsem,
LONG lReleaseCount,
PLONG plPreviousCount);

参考链接: http://dev.yesky.com/339/2292339.shtml


linux中mutex和semaphore的区别

Mutex的count有三种可能值。“1”代表unlocked,"0"代表lokced,负值代表可能的等待者。

Mutex有如下约定:




有且只有一个进程来持有。





有且只有owner本身可以unlock mutex(owner指针的作用)。





不允许递归锁。但是semaphore是允许的,MySQL在这块上,之前还有一个BUG:Bug #24745 InnoDB semaphore wait timeout/crash – deadlock waiting for itself





只能能过API来初始化,不允许通过memset和copying来初始化。





当持有mutex时,进程一般不退出;存放mutex的内存不能被释放。





通常不用于软硬件的中断。




五、总结


从上面的定义,可以看出,semaphore一般只是表示资源有多少,信息量有多少,用来类似于解决producer-consumer的问题和同步信号发送等。不能解决串行化问题。
而Mutex则是来解决互斥问题,保证某个资源或者代码片段的串行访问;因为只有owner才能释放锁。另外,有同学可能会问,Binary
semaphore也只有两种可能值,也可以实现互斥访问。但是要知道,Binary
semaphore还是没有owner指针。但有一点相似处,就是在ISR(Interrupt Service
Routine)系统中,semaphore或者mutex会被中断程序中断。


linux mutex互斥体和semaphore信号量的区别

mutex互斥体只用于保护临界区的代码(访问共享资源),而不用于锁之间的同步,即一个线程释放mutex锁后,马上又可能获取同一个锁,而不管其它正在等待该mutex锁的其它线程。
semaphore信号量除了起到保护临界区的作用外,还用于锁同步的功能,即一个线程释放semaphore后,会保证正在等待该semaphore的线程优先执行,而不会马上在获取同一个semaphore。
如果两个线程想通过一个锁达到输出1,2,1,2,1,2这样的序列,应使用semaphore, 而使用mutex的结果可能为1,1,1,1,1,2,2,2,111.....。


上一篇:秋意在心中:爱是永恒

下一篇:没有了