Windows线程间通信

来源:互联网 发布:什么软件可以淘宝返利 编辑:程序博客网 时间:2024/05/20 17:26

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) 使用内核对象进行线程通信

互锁函数和临界段都是属于用户态的通信,好处是速度很快,但是对许多应用程序而言是不足的,而使用内核对象进行通信速度较慢,其他的性能较好。使用内核对象进行线程通信的机理是:很多内核对象存在一个属性,用来表示该内核对象是已通知状态还是未通知状态,然后通常使用WaitForSingleObjectWaitForMultipleObjects来等待特定内核对象的已通知状态。

  • 事件对象

事件内核对象通常和WaitForSingleObject等联合使用,事件对象主要用于标志一个操作是否已经完成,其函数为:

HANDLE CreateEvent(

PSECURITY_ATTRIBUTES psa,

BOOL fManualReset,

BOOL fInitialState,

PCTSTR pszName);

第一个参数为安全属性,第二个参数用于设置创建一个人工重置事件(ture)还是自动重置事件,两者的区别在于:如果设置成人工重置事件,则需要使用SetEventResetEvent函数来将事件设置成已通知事件和未通知事件,当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度的;如果设置成自动重置事件,当使用SetEvent设置事件的通知状态时,在等待事件的线程中,只有一个线程被回复(哪个不确定),之后系统自动将事件设为未通知状态。第三个参数用于设置事件初始状态,第四个参数用于设置事件的名字。

  • 互斥对象

互斥量是一种内核对象,它能够确保线程拥有对单个资源的互斥访问权。它与临界段相同,但是互斥量属于内核对象,临界段属于用户对象,这意味着互斥要比临界段慢,但是不同进程中的多个线程能够访问单个互斥量。互斥量不同于所有其他的内核对象,互斥量中有一个线程ID,用于标志该互斥量属于哪个线程(即不论在哪个线程中创建了没有归属的互斥量,只要在某个线程中将互斥量变为未通知事件,那么这个线程就拥有这个互斥量,如果在该线程中释放了这个互斥量,那么该互斥量又变成游离状态,没有所属的线程,然后重复上面的过程),因此互斥量有一个“线程所有权”的概念,因此如果调用ReleaseMutex的线程不拥有互斥量,那么该函数不进行任何操作。

Win32 API互斥量函数:

创建一个互斥量:

HANDLE CreateMutex(

PSECURITY_ATTRIBUTES psa,

BOOL fInitialOwner,

PCTSTR pszName);

第一个参数安全属性,最后一个参数为互斥量的名字,第二个参数:

A) 如果设为FALSE,则表示没有线程拥有该互斥量,线程ID0,处于已通知 状态。

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);

原创粉丝点击