windows核心编程-关键段(临界区)线程同步

windows核心编程-关键段(临界区)线程同步

线程同步的方式主要有:临界区、互斥区、事件、信号量四种方式。

接下来我主要讲一下自己在学习windows核心编程中对于临界区线程同步方式的使用。

临界区线程同步在windows核心编程中被称为关键段线程同步,以下统称关键段
关键段是一小段代码,它在执行之前需要独占对一些资源的访问权。
缺点:能且只能用在一个进程中的多线程同步。可能陷入死锁,因为我们无法为进入关键段的线程设置最大等待时间。

接下来我介绍一些关键段线程同步的使用
先看一个事例代码

int g_nSum = 0;
CRITICAL_SECTION g_cs;

DWORD WINAPI FirstThread(PVOID pvParam)
{
  EnterCriticalSection(&g_cs);
  g_nSum = 0;
  for (int n = 0; n < 10000; ++n)
  {
    g_nSum += n;
  }
  LeaveCriticalSection(&g_cs);
  return g_nSum;
}

DWORD WINAPI SecondThread(PVOID pvParam)
{
  EnterCriticalSection(&g_cs);
  g_nSum = 0;
  for (int n = 0; n < 10000; ++n)
  {
    g_nSum += n;
  }
  LeaveCriticalSection(&g_cs);
  return g_nSum;
}
在使用关键段(CRITICAL_SECTION)时,只有两个必要条件:
1、想要访问资源的线程必须知道用来保护资源的CRITICAL_SECTION对象地址。
CRITICAL_SECTION对象可以作为全局对象来分配,也可以作为局部对象来分配,
或者从堆中动态地分配。
2、如何线程在试图访问被保护的资源之前,必须对CRITICAL_SECTION结构的内部
成员进行初始化。


关键段线程同步常用函数介绍
//1、首先我们要分配一个CRITICAL_SECTION对象,并进行初始化(使用关键段同步的线程必须调用此函数)
void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection )

//2、当知道线程将不再需要访问共享资源时,我们应该调用下边的函数来清理CRITICAL_SECTION结构
void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection )

//3、在对保护的资源进行访问之前,必须调用下面的函数
void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection )
//可以对上边的函数多次调用,表示调用线程被获准访问的次数

//4、也可以用下边的函数代替EnterCriticalSection
BOOL TryEnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection )
//通过返回值判断当前线程是否获准访问资源,线程永远不会进入等待状态,如果
//返回TRUE表示该线程获准并正在访问资源,离开时必须调用LeaveCriticalSection()

//5、在代码完成对资源的访问后,必须调用以下函数,释放访问权限
void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection )
//转载请注明文章来自:http://blog.csdn.net/windows_nt
以上访问方式(EnterCriticalSection方式)可能会使调用线程切换到等待状态,这意味着线程必须从用户模式切换到内核模式,这个切换开销非常大。为了提高关键段的性能,Microsoft把旋转锁合并到了关键段中。因此,当调用EnterCriticalSection的时候,它会用一个旋转锁不断地循环,尝试在一段时间内获得对资源的访问权,只有当尝试失败时,线程才会切换到内核模式并进入等待状态。

//1、为了在使用关键段的时候同时使用旋转锁,我们必须调用下面的函数来初始化关键段
BOOL InitializeCriticalSectionAndSpinCount( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount )
//第二个参数dwSpinCount表示我们希望旋转锁循环的次数。

//2、我们也可以调用下面的函数来改变关键段的旋转次数
DWORD SetCriticalSectionSpinCount( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount )
//如果主机只有一个处理器,函数会忽略dwSpinCount参数

Slim读/写锁

SRWLock允许我们区分那些想要读取资源的值的线程(读取者线程)和想要更新资源的值的线程(写入者线程)。
//1、首先我们要分配一个SRWLOCK对象并用下边函数初始化它。
void InitializeSRWLock( __out PSRWLOCK SRWLock )

//2、请求对保护资源的独占访问权(写权限)
void AcquireSRWLockExclusive( __inout PSRWLOCK SRWLock )

//3、完成对资源的更新后,应该解除对资源的锁定
void ReleaseSRWLockExclusive( __inout PSRWLOCK SRWLock )

//4、对应的读者线程函数如下
void AcquireSRWLockShared( __inout PSRWLOCK SRWLock )
void ReleaseSRWLockShared( __inout PSRWLOCK SRWLock )
与关键段相比,PSRWLOCK缺乏下面两个特性
1、不存在TryEnter(Share/Exclusive)SRWLock之类的函数,如果锁已经被占用,那么调用会阻塞调用线程
2、不能递归的获得PSRWLOCK。也就是说,一个线程不能为了多次写入资源而多次锁定 资源,如后再多次释放对资源的锁定。

线程同步性能排序(由高到低)
volatile读取 -->volatile写入-->Interlocked API(原子方式)-->SRWLock-->关键段-->内核对象

互斥器的介绍-----windows核心编程-互斥器(Mutexes)

你可能感兴趣的:(windows核心编程-关键段(临界区)线程同步)