解决多线程中11个常见问题(3)

2019-01-19 17:48

public class ImmutableStack { private readonly T m_value;

private readonly ImmutableStack m_next; private readonly bool m_empty;

public ImmutableStack() { m_empty = true; } internal ImmutableStack(T value, Node next) { m_value = value; m_next = next; m_empty = false; }

public ImmutableStack Push(T value) { return new ImmutableStack(value, this); }

public ImmutableStack Pop(out T value) { if (m_empty) throw new Exception(\ return m_next; } }

节点被推入时,必须为每个节点分配一个新对象。在堆栈的标准链接列表实现中,必须执行此操作。但是要注意,当您从堆栈中弹出元素时,可以使用现有的对象。这是因为堆栈中的每个节点是固定不变的。

固定不变的类型无处不在。CLR 的 System.String 类是固定不变的,还有一个设计指导原则,即所有新值类型都应是固定不变的。此处给出的指导原则是在可行和合适的情况下使用不变性并抵抗执行变化的诱惑,而最新一代的语言会使其变得非常方便。

纯度

即使是使用固定不变的数据类型,程序所执行的大部分操作仍是方法调用。方法调用可能存在一些副作用,它们在并发代码中会引发问题,因为副作用意味着某种形式的变化。通常这只是表示写入共享内存,但它也可能是实际变化的操作,如数据库事务、Web 服务调用或文件系统操作。在许多情况下,我希望能够调用某种方法,而又不必担心它会导致并发危险。有关这一方面的一些很好示例就是 GetHashCode 和 ToString on System.Object 等简单的方法。很多人都不希望它们带来副作用。 纯方法始终都可以在并发设置中运行,而无需添加同步。尽管纯度没有任何常见语言支持,但您可以非常简单地定义纯方法:

1. 它只从共享内存读取,并且只读取不变状态或常态。 2. 它必须能够写入局部变量。

3. 它可以只调用其他纯方法。

因此,纯方法可以实现的功能非常有限。但当与不变类型结合使用时,纯度就会成为可能而且非常方便。一些函数式语言默认情况下都采用纯度,特别是 Haskell,它的所有内容都是纯的。任何需要执行副作用的内容都必须封装到一个被称为 monad 的特殊内容中。但是我们中的多数人都不使用 Haskell,因此我们必须遵照纯度约定。

隔离

前面我们只是简单提及了发布和私有化,但它们却击中了一个非常重要的问题的核心。由于状态通常在多个线程之间共享,因此同步是必不可少的(不变性和纯度也很有趣味)。但如果状态被限制在单个线程内,则无需进行同步。这会导致软件在本质上更具伸缩性。

实际上,如果状态是隔离的,则可以自由变化。这非常方便,因为变化是大部分 C 风格语言的基本内置功能。程序员已习惯了这一点。这需要进行训练以便能够在编程时以函数式风格为主,对大多数开发人员来说这都相当困难。尝试一下,但不要自欺欺人地认为世界会在一夜之间改为使用函数式风格编程。

所有权是一件很难跟踪的事情。对象是何时变为共享的?在初始化时,这是由单线程完成的,对象本身还不能从其他线程访问。将对某个对象的引用存储在静态变量中、存储在已在线程创建或排列队列时共享的某个位置或存储在可从其中的某个位置传递性访问的对象字段中之后,该对象就变为共享对象。开发人员必须特别关注私有与共享之间的这些转换,并小心处理所有共享状态。


解决多线程中11个常见问题(3).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:会计一二章作业

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: