Software Transactional Memory

soft transactional memory

1 Software Transactional Memory

原文 编写并发软件很困难,由于运行顺序无法控制,产生的错误很难复现,同时,全面的测试也需要花费更多的精力。 多线程软件编写通常有两种划分方法,

  • 一种方法是把单一的任务划分为多个独立的小片段,然后每个线程独立执行各个片段;
  • 另一种方法是协调多个任务同步执行;

显然,后一种方法更难实现,而且不容易追踪和差错。

编写并发软件有许多种模型。现在最长用的是:locks, actors, transaction memory。这篇文章简单介绍了三种模型,然后会深入探讨soft-base Transactional Memory 本文的目的有:

  • 介绍STM
  • STM的性能特点给出证明
  • 推荐多种语言对STM模型的支持
  • 推荐对STM感兴趣的开发者实现Clojure的STM模型
  • 提供开发者对Clojure中的STM模型足够的认识,让他们开发出实用的STM开发测试工具,如跟踪事务处理次数等。

1.1 Lock-base Concurrency

锁机制的并发任务,一般多线程访问共享内存。最简单的模型为:在访问共享内存代码执行前,首先要获取互斥锁,保证同一时间只有一个线程访问这段共享内存。 优势:

  • 开发者可以显示的控制锁的行为
  • 许多开发者都很熟悉这一模型
  • 许多开发语言内置支持此模型

劣势:

  • 很难控制什么时候需要用锁
  • 锁使用异常情况下, 互斥锁变量能够被访问
  • 死锁
  • 错误状态恢复比较复杂,开发者需要记住释放互斥锁
  • 正常的状态同步方法会组合会成为复杂的方法,但是却需要额外的互斥锁
  • 悲观锁。把共享区的访问建立在同时访问只有一个线程的基础上,这并不总是正确的,而且这一假设降低了并发的效率。

1.2 Actor-based Concurrency

Actor Mode机制是另一个并发模式访问共享内存的解决方法。把Actor视为一个实体,即一个独立的进程或者轻量级的线程。 actor不会主动访问共享内存区域,而是被动的接收异步消息。当一个actor接收到消息后,可以做以下几种操作:

  • 新建actor
  • 发送消息给其他actor
  • 定义如何处理下一个接收到的消息

Actor-based concurrency的优势:

  • 由于actors不会访问共享区域,共享区的访问不再需要同步

劣势:

  • 由于actor不会直接访问共享内存,通过发送消息共享,消息内容会很大
  • 不同的actor持有状态不同

Actor mode被Erlang, Haskell, Scala所支持

1.3 Transactional Memory

Transactional Memory是另一个并发模式访问共享的解决方法。这种方法简化了开发并发软件的复杂度。 Transactional Memory的概念类似于DB Transactions,提供了ACID几种特性:

  • "A" is for atomic,指所做的操作完全成功,或者完全不成功。
  • "C" is for consistant,指数据在Transaction期间,从开始到结束都是一致的,不会中途修改。
  • "I" is for isolated,指在Transaction期间修改的数据不会被其他进程所见,只有commit后才会。
  • "D" is for durable,指一旦commit后,所做修改的数据不会丢失,即使在软硬件出错的情况下。

Transactions根据代码不同有许多不同的实现,在其他不同的线程视角,在做commit操作后,所有的共享内存修改都是同时生效的。但是在 commit之前,其他线程并不能看到所做的修改。这是因为所有的操作都是在一块独立的镜像区域操作的。例如:Transaction A对一块 内存区域做了修改,同时Transaction B也在对这块内存做修改,并且B在A之前做了commit操作,这是A会用B所做修改后的数据重新做之前的 修改操作。这些操作特性保证了Transactional Memory的Atomic,Consistant 和Isolated。注意Transational Memory并不能保证durable 的特性,因为软件缓存异常或者硬件异常,都会引起数据的丢失。 Transactional Memory是乐观的,所有它所修改的数据,它都认为其他线程没有修改过,如果被其他线程修改过,那么它会重新读取最新的数据, 并在新数据基础上做修改操作。这些特性保证了所有的transaction操作不会产生不可恢复等副作用。 优势:

  • 可以增加并发运行的效率
  • 编写并发软件更简单
  • 实现可以保证不会发生deadlock、livelock、race condition的发生

劣势:

  • 存在许多潜在的transaction retries,这会耗费许多资源
  • 需要缓存许多transaction中间状态值
  • 分析工具匮乏

1.4 Clojure有4种引用类型:

  • Var,Var拥有和其他线程共享的原值和线程独有的值。def,set!,binding可以用来修改Var变量值。一般Var变量都是建议用作常量。一种比较 常用的修改Var变量值的是当Var变量作为配置参数时。
  • Atom,Atom在所有线程中拥有唯一的值。所以对它的访问都是需要原子性。reset!,compare-and-set!,swap!函数可以用来操作Atom变量。
  • Agent, Agents在所有线程中都是相同的值。修改Agents变量值是通过调用异步函数实现的。发送给相同Agent的动作会排在同一队列中, 所以同一时间只有一个Agent动作在执行。send和send-off是用来操作Agent变量的,当动作异步执行后,立即会有返回值。这两个操作函数 唯一的区别是send是固定Queue大小的,而send-off的Queue大小是变化的。await函数会终止当前线程的操作,直到发送的操作全部发送到 指定的Agent;await-for函数唯一的区别是可以设置timeout。Agent的修改动作都是在Transaction内部进行的,只有当commit完成后 才会返回。这一特点可以用来执行有副作用的transaction,像IO操作等。
  • Ref,Ref在所有线程中拥有相同的值。所有修改操作都在STM Transaction中进行。ref-set,alter,commute几个是操作函数。

Date: 2013-07-27 22:26:42 中国标准时间

Author:

Org version 7.8.11 with Emacs version 24

Validate XHTML 1.0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章