c# 非阻塞算法_基于无锁的C#并发队列实现

最近开始学习无锁编程,和传统的基于Lock的算法相比,无锁编程具有其独特的优点,Angel Lucifer的关于无锁编程一文对此有详细的描述。

无锁编程的目标是在不使用Lock的前提下保证并发过程中共享数据的一致性,其主要的实现基础是CAS操作,也就是compare_and_swap,通过处理器提供的指令,可以原子地更新共享数据,并同时监测其他线程的干扰,.Net中的对应实现是InterLocked.CompareExchange函数。

既然不使用Lock,那在无锁编程中要时刻注意的是,代码可能在任意语句中被中断。如果是单个变量,我们可以使用 InterLocked.XXX 保证操作的原子性,但是如果有多个操作要完成的话,简单地组合 InterLocked.XXX 是远远不够的。通常的原则是对函数中用到的共享变量,先在代码开始处用局部变量保存它的内容,在后面更新共享变量时,使用前述变量来判断其是否发生了改变,如果共享变量发生了改变,那么我们可能需要重试,或者在某些可能的情况下,当前线程可以"帮助"其他更新中的线程完成更新。

从上面可以总结出无锁算法的两个基本特征:

1. 无锁算法总是包含一个循环结构,以保证更新失败后重试

2. 无锁算法在更新共享变量时,总是使用CAS和原始值进行比较,以保证没有冲突

下面是按照Michael-Scott算法实现的并发队列,其中的Dequeue算法在IBM的非阻塞算法一文中有详细介绍。代码如下:

Code1

publicclassConcurrentLinkedQueue2

{3privateclassNode4

{5internalK Item;6internalNodeNext;78publicNode(K item, Nodenext)9

{10this.Item=item;11this.Next=next;12        }13    }1415privateNode_head;16privateNode_tail;1718publicConcurrentLinkedQueue()19

{20        _head=newNode(default(T),null);21        _tail=_head;22    }2324publicboolIsEmpty25

{26

get{return(_head.Next==null); }27    }2829publicvoidEnqueue(T item)30

{31        NodenewNode=newNode(item,null);32while(true)33

{34            NodecurTail=_tail;35            Noderesidue=curTail.Next;3637//判断_tail是否被其他process改变38if(curTail==_tail)39

{40//A 有其他process执行C成功,_tail应该指向新的节点41if(residue==null)42

{43//C 如果其他process改变了tail.next节点,需要重新取新的tail节点44if(Interlocked.CompareExchange>(45refcurTail.Next, newNode, residue)==residue)46

{47//D 尝试修改tail48Interlocked.CompareExchange>(ref_tail, newNode, curTail);49return;50                    }51                }52else53

{54//B 帮助其他线程完成D操作55Interlocked.CompareExchange>(ref_tail, residue, curTail);56                }57            }58        }59    }6061publicboolTryDequeue(outT result)62

{63        NodecurHead;64        NodecurTail;65        Nodenext;66do67

{68            curHead=_head;69            curTail=_tail;70            next=curHead.Next;71if(curHead==_head)72

{73if(next==null)//Queue为空74

{75                    result=default(T);76returnfalse;77                }78if(curHead==curTail)//Queue处于Enqueue第一个node的过程中79

{80//尝试帮助其他Process完成操作81Interlocked.CompareExchange>(ref_tail, next, curTail);82                }83else84

{85//取next.Item必须放到CAS之前86result=next.Item;87//如果_head没有发生改变,则将_head指向next并退出88if(Interlocked.CompareExchange>(ref_head,89                        next, curHead)==curHead)90break;91                }92            }93        }94while(true);95returntrue;96    }97}98

根据自己的测试(双核CPU),在轻度和中度争用情况下,无锁算法比基于锁的算法性能好很多,在争用非常严重的情况下(100个并发线程以上/每CPU),基于锁的算法性能开始显示出优势,因为一旦发生争用,基于锁的算法会立刻切换到其他线程,而无锁算法会进入下一次循环,导致CPU的占用。但是如此严重的争用在实际中并不多见,并且可以采用SpinWait的方法加以改进。基于锁的算法在测试中曾经出现过类似死锁的现象,无锁算法则完全没有出过类似问题,另外,处理器核心越多,基于锁的算法效率越差。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值