高级线程同步。刚刚简单的说了一下旋转锁,现在又来说旋转锁的坏,旋转锁的问题在于等待的线程一直在执行毫无用处的该死的死循环,浪费CPU的时间,这肯定是不能容忍的,虽然曾经一度容忍过它。当一个线程需要某个资源,而这个资源被另一个线程占用时,如果这个线程等了一会儿还不能获得这个资源,那么这个线程应该被切换到等待状态,让系统充当该线程的代理,当该资源可以被使用时,系统会将该线程唤醒,然后该线程可以独占该资源。而实现这一功能的是关键段。

  关键段。关键段是一小段代码,在执行之前需要独占对一些共享资源的访问,这种方式可以让多行代码以原子的方式进行访问,当有一个线程对访问这段代码时其他线程只能等待。使用关键段的步骤如下:

  CRITICAL_SECTION g_cs;//构造一个CRITICAL_SECTION实例
  InitializeCriticalSection(&g_cs);//初始化g_cs的成员
  EnterCriticalSection(&g_cs);//进入关键段
  LeaveCriticalSection(&g_cs);//离开关键段
  DeleteCriticalSection(&g_cs);//清理g_cs

  EnterCriticalSection会检查结构CRITICAL_SECTION的成员变量,这些成员表示是否有线程正在访问资源,以及哪个线程正在访问资源,EnterCriticalSection会进行一些测试。如果没有线程正在访问资源,EnterCriticalSection会更新变量成员,以表示已经有线程正在访问资源,并马上从EnterCriticalSection返回,继续执行关键段中的代码,如果变量成员表示已经有线程正在访问资源,那么EnterCriticalSection会使用一个事件内核对象把线程切换成等待状态,等待状态的线程是不会浪费CPU的时间的,系统会记住这个线程想要使用这个资源,一旦当前线程调用LeaveCriticalSection,系统会自动更新CRITICAL_SECTION的成员变量,并将等待的线程切换成可调度状态。

  LeaveCriticalSection会检查结构CRITICAL_SECTION的成员变量并将计数器减一,如果计数器变为0,LeaveCriticalSection会更新成员变量表示现在没有线程访问资源,若有等待的线程,则将等待的线程切换成可调度的状态。

  当一个线程进入关键段时,若有线程正在访问关键段,那么系统会将新的线程切换成等待状态,这意味着将线程从用户模式切换成内核模式,这个切换的开销大约是1000个CPU周期,这个开销其实是很大的,所以在EnterCriticalSection内部使用旋转锁,并不是马上将线程切换成等待状态,而是先用旋转锁试探一些,看线程是否释放了对资源的访问,如果释放了,新的线程不用被切换成等待状态了,可以直接访问资源了,也是说花了旋转锁轮询的时间,如果旋转锁轮询了一段时间,线程还是没有释放资源,对不起系统不会让它继续轮询了,因为系统也不知道还要轮询多久,毕竟轮询一直都是在消耗CPU的时间,系统会停止轮询,将新的线程切换成等待状态,当前一个资源释放对资源的访问,系统会将新的线程切换成可调度状态。

  Silm读/写锁。SRWLock的目的和关键段是一样的,是对资源的保护,不让其他线程访问。不同的是,它区分线程是读线程还是写线程。我们都是知道,一个资源可以同时被多个线程同时读,是不能同时读,或是读写。也是是说写必须是独占的方式,而读可以以共享的方式访问。

  读写锁调用的函数如下,跟关键段差不多,我不废话了。

  RTL_SRWLOCK lock;
  InitializeSRWLock(&lock);
  AcquireSRWLockExclusive(&lock);//独占的方式访问
  ReleaseSRWLockExclusive(&lock);
  AcquireSRWLockShared(&lock);//共享的方式访问
  ReleaseSRWLockShared(&lock);