系统中的所有线程都要访问系统资源,一个线程霸占某个资源,其他需要该资源的线程不能完成自己的任务;另外如一个线程在读取某块内存中的数据,而另一个线程又正在修改这块内存的值,这同样不是我们想要的,所以线程之间必须要有一套自己的规则,不然凌乱了。线程之间需要通信,如A线程霸占某个B线程需要的资源X,在A占用期间,B线程只能等待,或处于挂起状态,当A线程用完资源X后,系统会告诉线程B,资源X可以用了,或是将处于挂起状态的线程B唤醒,然后线程B获得对资源X的控制权,其他想用资源X的线程得经历B刚才的遭遇。当多个线程同时需要某个资源时必须遵守下面两个规则:

  1、多个线程“同时”访问资源,不能破坏资源的完整性。

  2、一个线程需要通知其他线程某项任务已经完成。

  原子访问:Interlocked系列函数。多线程编程大部分情况与原子访问有关,即一个线程在访问某个资源时,确保没有其他线程能访问该资源。

  增量函数InterlockedExchangeAdd结构如下:

InterlockedExchangeAdd(
    unsigned long volatile *Addend,//被增量变量的地址
    unsigned long Value//增量值
)

  Volatile表示每次都成内存中读取数据,而不会从高速缓存中读取数据,如一个全局变量,在一个多线程函数中被修改,在多核CPU中,这个变量可能在多个CPU的高速缓存中都有副本,如果不用volatile修饰,那么可能会因为优化的原因,CPU不会读内存中的数据,而是直接从高速缓存中读取数据,在这种情况下,很可能这个值已经被修改了,这样CPU读取到的不是新的数据,程序肯定会出错,用volatile修饰后,这个变量的所有高速缓存会失效,不会出现这种问题。在多线程编程中volatile作用非常大,效率也高。但他是只能修饰单个变量,不能修饰代码段。

  InterlockedExchangeAdd执行的速度是非常快的,只需要占用几个CPU周期。用InterlockedExchangeAdd来修改某个变量的值,好像有点大材小用了,因为用Volatile足够了,简单迅速。但在实现旋转锁时InterlockedExchange非常有用。旋转锁的代码大致如下:

bool sourceIsUse=false;
        void fun()
        {
            //一直等待直到资源可用
            while(InterlockedExchange(&sourceIsUse,true)==true)
            {
                Sleep(0);
            }
            //访问资源的操作
            ......

            //资源用好了,打开锁,让其他等待的资源访问
            InterlockedExchange(&sourceIsUse,false);
        }

  InterlockedExchange:将第一个参数的值修改成第二个参数的值,返回第一个参数原来的值。在第一个线程来的时候,它顺利的闯过了While循环,并上了锁,导致while始终为true,后来的线程一直在while里面打转,当前面的线程用完之后,他会把锁打开,然后新来的线程可以跳出while循环,并上锁(在等待时一直在上锁),开锁独占资源了,新来的线程又开始等待。像大厦前门的旋转门,一拨人进去之后,后面的人只能在外面等,等里面的人出去之后,后面的人也可以进去了,周而复始。

  高速缓存行。当CPU从内存中读取一个字节时,它并不是真的只读一个字节,而是读取一个高速缓存行,一个高速缓存行可能是32个字节、64个字节或是128个字节,它始终读取的字节数是32的整数倍,这样CPU不用非常频繁的读取内存,从而提高程序的性能,当CPU访问某块内存是它会访问这块内存旁边的内存的概率是非常大的,于是一起读了。更多关于数据对齐的信息请看我的文章《数据对齐》。