小城岁月 2011-10-26 16:58
Node<T> curTail = _tail;
35 Node<T> residue = curTail.Next;
36
37 //判断_tail是否被其他process改变
38 if (curTail == _tail)
curTail 和_tail 是引用对象指向一块内存,怎么会有不相等的时候呢?
我在if操作开了2个线程,一个去改变_tail(先执行),一个来执行if(curTail==_tail) (sleep 20秒)结果是相等的。
yuping322 2009-08-27 11:03
Interlocked,java里面有这个吗?
游客123 2009-06-10 18:16
文中无锁也仅仅是指无法获得锁时线程不休眠,等同于内联了自旋锁,对于占用锁时间非常少的情况节约了上下文的切换.
比较交换,在硬件实现上,也是一个锁,也要面对一个CPU独占内存总线,其他CPU无法访问的瞬时串行化. 虽然没有见过严谨的证明,但猜测不太可能有通用的锁变无锁算法的办法.
真正的硬无锁,还比较浮云.
在路上的牛 2008-12-03 20:04
@蛙蛙池塘
短路是正常的,当初我也来回想了好久,呵呵。
if (residue == null) 是为了防止其他线程修改trail.Next,residue可以不为null是因为多线程的原因,而且算法又没有加锁,所以有可能发生这样的情况:
1. 线程a读取到curTail.Next,被挂起,
2. 线程 b也开始Enqueue,并执行完curTail.Next = newNode,挂起,
3. 线程a继续执行,此时就会发现residue != null了,此时将转到55行
vicqqq 2008-12-03 10:34
...........
蛙蛙池塘 2008-12-02 23:56
//A 有其他process执行C成功,_tail应该指向新的节点
41 if (residue == null)
这句,为什么residue == null就表示C执行成功呀,啥叫C执行成功呀,residue在整个Enqueue方法里只有一次赋值操作吧,即便把newNode赋值给curTail.Next,而residue又指向curTail.Next,但newNode也不是null呀?
蛙蛙池塘 2008-12-02 23:06
思维有些短路,看不太懂。
空明流转的马甲 2008-12-02 12:17
除了一些固定场景以外,多线程程序的正确性主要还是依赖程序员自身的经验。
固定场景的解决方案一般也就是诸如读写锁,消息队列,Wait/Condition一类的常见手法。最好的并行还是在于问题本身就是可并行的。
如果没有Lazy Evaluation的话,Invariant Object实际上实用价值不大。
在路上的牛 2008-12-02 11:49
@Angel Lucifer
多谢指点,Write-Copy的确应该是Copy-on-Write,看到中文译名,想当然了,呵呵。
至于volatile的用法,这里volatile read/write的使用有什么问题,很想听听你的看法
Angel Lucifer 2008-12-02 11:15
这篇比起上篇来说差了不少,呵呵。
很多地方都不对,比如讲解 Write-Copy 技术(我猜这里应该是 Copy on Write)和 volatile 的都不对。时间匆忙,不写理由了。
在路上的牛 2008-12-02 10:19
@shawnliu
我这些总结就是对《Java并发编程:设计原则与模式》的一个读书笔记。除了这本书,《java并发编程实践》,IBM的developerworks,园子里几位博客的文章里面都有大量的参考资料。
shawnliu 2008-12-02 02:36
@Angel Lucifer
--引用--------------------------------------------------
Angel Lucifer: <p>写得蛮好。</p>
<p>不过有些地方值得商榷。</p>
<p>对于不可变对象来说,俺的观点是假如你使用命令式编程语言,可以使用,但应当限制性使用。因为一旦这些对象泛滥,内存占用直线上升。这样势必造成 Cache 命中率降低,CPU 频繁读取内存。而这样多的 I/O 操作对系统性能影响巨大。这也是当今类似 Erlang 这样的函数式语言性能不高的原因之一。</p>
<p>另外,增加 CPU 单元是有意义的。虽然加锁之后的串行不可分割,但是对于系统整体来说,仍然可以增加并行性。具体可以参考:</p>
<a href="http://www.cnblogs.com/lucifer1982/archive/2008/08/24/1274928.html" target="_blank">并行思维[II]</a>
--------------------------------------------------------
讲的很好 cpu cache命中率的降低这个观点倒是很新颖
shawnliu 2008-12-02 02:31
很高心有深度研究并发的文章 希望能和lz交流一下 我也认真研究过一段时间多线程 也在工作中不少场景用到过 但是lz很多观点的总结还是让我耳目一新 请问lz是参考了哪些书籍和文章呢 可否share一下
在路上的牛 2008-12-02 00:57
@Angel Lucifer
谢谢你的鼓励,呵呵。
同意你的看法,不可变对象容易导致对象数量太多,内存占用也直线上升。这个要看应用的场合是否合适。
文中“增加内存单元是没有意义的”这句话,指的是加锁后的串行化代码,具体对并行性的加速比,还是要看代码中并行和串行代码的比例而定。其实我开始仔细了解多线程,也是从你的文章开始的,你的几篇文章我都看过,学到很多东西。
在路上的牛 2008-12-02 00:51
@Da Vinci
这篇是想先建立一个架子,具体的策略在后面介绍,所以代码也多在后面
Angel Lucifer 2008-12-01 23:41
写得蛮好。
不过有些地方值得商榷。
对于不可变对象来说,俺的观点是假如你使用命令式编程语言,可以使用,但应当限制性使用。因为一旦这些对象泛滥,内存占用直线上升。这样势必造成 Cache 命中率降低,CPU 频繁读取内存。而这样多的 I/O 操作对系统性能影响巨大。这也是当今类似 Erlang 这样的函数式语言性能不高的原因之一。
另外,增加 CPU 单元是有意义的。虽然加锁之后的串行不可分割,但是对于系统整体来说,仍然可以增加并行性。具体可以参考:
并行思维[II]
上不了岸的鱼{ttzhang} 2008-12-01 21:51
不错支持一下,期待后面的内容...
Da Vinci 2008-12-01 20:55
LZ写的挺好,就是太短了,还没看多少就没有了。
可以配上点代码
onlyone 2008-12-01 20:27
不错期待下一篇啊
xiaopohai_long 2008-11-21 12:52
不错,收藏。。。。
在路上的牛 2008-11-21 09:42
@Anders Liu
其实我觉得在绝大多数环境下,可以不用spin wait,毕竟如果争用如此之高的话,要么程序有可以改进之处,要么可以考虑增加硬件的多核来解决。
另外即使加入了spin wait,与锁也有很大的区别,因为在任意时刻,每个CPU单元上总有1个线程能向前推进,多个线程互相死锁的情况是完全不存在的
在路上的牛 2008-11-21 09:37
@刘守照
无锁编程的优势之一正是不会死锁,从上面的算法也可以看出来。CAS目前在 x86 的 cpu 上,是通过 "lock cmpxchg" 指令完成的,粒度非常小,如果多个CAS同时更改一个共享变量,也不可能出现死锁的问题。
doyle[游客] 2008-11-21 09:26
类似自旋锁的概念
Anders Liu 2008-11-21 07:58
spin wait就不是锁了么?
刘守照 2008-11-21 00:38
粒度太小了 太难测试了 而且相对比较容易产生deadlock
刘守照 2008-11-21 00:37
看的有点懂了 如果对并发要求很高的话 降低锁的粒度就好了 你这个无锁里面也用到锁了啊 只是粒度比通常monitor等处理的细腻的多了 使得throughput好了很多
刘守照 2008-11-21 00:29
啥叫无锁编程?可以避免使用锁来做同步么
中华鹰 2008-03-25 12:45
多重继承感觉并没必要,这对于减少代码的混乱有很大帮助,我想微软也是出于这方面的考虑才不在C#中实现多重继承的吧。
所以,再用变相的方法实现多重继承,我觉得更没必要。
如果确实有必要,是不是可以通过某些设计模式来解决呢?
在路上的牛 2008-03-25 09:56
@Cat Chen
在接口上提供默认实现一般情况下并不需要,不应该滥用,但是这种方式在某些情况下是有价值的。
微软自己也有这样的例子,System.Linq中的IQueryable<T>就是一例,它自己没有声明任何方法,但是在Queryable类中为IQueryable<T>提供了大量的扩展方法。我自己也感觉以前的框架中某些接口是可以使用一些扩展方法的。
使用这样的方法,需要对接口的设计有比较全面的考虑。
henry 2008-03-25 09:50
我们在系统设计时经常会抽象出一些接口,并为接口提供一个抽象类作为默认的实现,然后实际使用的类可以从抽象类派生。
很大程度上是减低二次扩展的复杂性,接口是一个纯约束对外的规则,并不存在任何的实现。实现抽象类作为默认可以提供后期扩展内部需要的很多功能。
dali 2008-03-25 09:39
楼主的思路绝对创新
xeme009 2008-03-25 09:04
不知所云,还C# 3.0,C#1.0就可以做到,典型的标题党
金色海洋(jyk) 2008-03-25 07:34
同意楼上。
曲滨*銘龘鶽 2008-03-25 01:25
还是伪 多集成;
添加的这个东西和
public static void TestB(this ITestB obj)
和
public static void TestB(ITestB obj)
原则上没啥两样、就是用着舒服了一点
用多了看代码时候迷糊,尤其是看别人的代码的时候
就和js似的、别人动态在那个对象上加了个东西有时候找半天
不过在VS里基本没这问题;有图标可以看见
Cat Chen 2008-03-25 00:27
@在路上的牛
你自己已经说出了,为什么不应该在接口上做默认实现。Framework Design Guideline说明了,建议的做法正式抽象的DbCommand实现IDbCommand,再派生出子类。不应该尝试直接在IDbCommand上面添加默认实现。
Angel Lucifer 2008-03-24 22:56
说实话,还真没用到过多继承,不过楼主给出了一个思路,Thanks。
在路上的牛 2008-03-24 22:43
这个方法是有很多局限。而且扩展方法是容易把人搞混,当初看Linq的代码,一个接口的实现方法半天没找到,后来才想起可能会在某个静态类的扩展方法中。。。
坏人 2008-03-24 21:36
呵呵,但是脱离OO的继承,已经很大程度失去了原有的意义嘛.
目前对扩展方法持谨慎态度,怕会导致一些不必要的混乱..
lovecherry 2008-03-24 21:31
调用类的方法时编译器先从类中找,找不到再找扩展方法,再找不到就编译错误。
在路上的牛 2008-03-24 21:27
@Dflying Chen
因为很多时候,对接口的方法我们是可以提供一个默认的实现的,如果不提供这样一个抽象类,那每个实现接口的类都要写上许多一样的方法实现,是个不好的设计。System.Data下的许多类都是按照接口 - 抽象类 - 具体类的方式设计的,比如IDbCommand - DbCommand - SqlCommond/ODBCCommand/OLECommand
再比如在ORM框架中,我们可以为所有的DataObject设计1个Interface,里面可能有基本的DataState, AccpetChange(),Original...,接下来也会提供一个抽象类来实现这些方法,实际的业务对象从此抽象类派生,而不是每个业务对象都为这些功能写一套一样的实现代码。
Dflying Chen 2008-03-24 20:44
我们在系统设计时经常会抽象出一些接口,并为接口提供一个抽象类作为默认的实现,然后实际使用的类可以从抽象类派生。
----------
为什么要这样做呢?能详细说明一下么?