BOOST源码笔记(5) - 最小的代价实现同步-无锁编程,看boost中的interlocked_bit_test_and_set实现

来源:互联网 发布:js函数继承 编辑:程序博客网 时间:2024/04/29 17:32

如题,如何用最小的代价来实现同步呢,即无锁编程(主要是使用原子操作替代锁来实现对共享资源的访问保护),

我们来看看下面的代码。

首先,我们需要是在这个函数内部对地址x中的值进行位测试并将其对应的位置位。  这里的关键是我们必须先读出*x的值,作位或运算之后,将得到的值对*x赋值。而在多线程环境中,在读出*x后也许有另外的线程对*x进行写操作,这样在给*x赋值的时候就会把对旧的值位或的结果赋给*x,造成错误。

 

这里boost是采用一个循环,将读出的值先保存,这样,读出后的值就为一个不变量,然后计算位或,当然也为不变量。接着用win32中的atom函数interlockedCompareExchange()来对比*x是否在被读出值后改变,如果没改变,则赋值;反之,则更新被保存的变量,再重复判断,直到第前面的情况发生。

 

也许有人会说,如果我这样写呢,                   

long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(x,*x|value,*x);

是不是就可以避免读和写不一致。错!你这样还是读了共享变量,而且是2次!2次读出的值都可能不一样,使得可能被位或地值都不一致。而且是在进入atom函数后才是同步代码,而读共享变量值得时候是在函数外部,还需要压栈,完全可能不同步。

 

inline bool interlocked_bit_test_and_set(long* x,long bit)
            {
                long const value=1<<bit;
                long old=*x;
                do
                {
                    long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(x,old|value,old);
                    if(current==old)
                    {
                        break;
                    }
                    old=current;
                }
                while(true);
                return (old&value)!=0;
            }

 

以上的函数所用的方法保证了对一个共享值的读-针对读出的值作操作-将操作后的值写回,这3步能够保持同步。我可以类举出win32中interlockedExchangeAdd() <即i++>的实现方法:

 

inline void interlockedExchangeAdd(long* x)
            {
                long old=*x;
                do
                {
                    long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(x,old + 1,old); //

比较并交换操作的作用是在一个原子操作内,先将目标值和老的值进行比较,如果相等就将新的值赋给目标变量。目的是为了防止将新的值赋给目标变量前,在读写操作之间目标变量遭到其他线程的修改。


                    if(current==old)
                    {
                        break;
                    }
                    old=current;
                }
                while(true);
            }

 

 

以上就是lock-free编程中的经典的CAS(compare and swap)的实现,除此外Lock-Free编程还有三大优点:

  • 线程中止免疫:杀掉系统中的任何线程都不会导致其它线程被延迟。
  • 优先级倒置免疫:所谓“优先级倒置”就是指一个低优先级线程持有了一个高优先级线程所需要的互斥体。这种微妙的冲突必须由OS内核来解决。而等待无关和锁无关算法则对此免疫。
  • 死锁免疫:因为没有使用锁,所以也就不存在死锁的可能。但是乐观的并发,可能会导致活锁。

虽然Lock-Free编程非常困难,但是它通常可以带来比基于锁编程更高的吞吐量。所以Lock-Free编程是大有前途的技术。它在线程中止、优先级倒置以及信号安全等方面都有着良好的表现。

 

但是CAS会出现ABA问题,这就不在我们的讨论范围内了。转个ABA的解决:

 

即定义额外的标识之类得方法来区别第一次A和第二次的A(不重用A),使得CAS能够察觉变化。