归档之于 ‘ 2013 年7 月 ’
C1X 系列 : 多线程 (N1494)
感谢林永听投递本文。 校对:方腾飞
1. 关于 C1X 标准
C1X 是 C 语言的下一个标准,用于取代现有的 C99 标准。 C1X 是一个非正式名字,该标准仍在制订中,最新的工作草案是 N1494 ,发布于 2010 年 6 月。与 C99 相比, C1X 在语言和库上有显著的变化,本文重点分析 N1494 草案中的多线程部分。
2. 呼之欲出的多线程
不瞒你说, C99 标准里面的内存模型仍然是单线程的,即所有代码都运行在一个线程(进程)内。也许,你简直不敢相信这个是真的,因为说不定你每天都与多线程打交道, 使用 _beginthread , CreateThread 或 pthread_create 等不同平台的函数去创建线程。当你担心每个变量被编译器优化时,不得不加上 volatile 修饰符时,或者加上内存栅栏 (memory barrier) ,都印证了 C99 为单线程的内存模型。
32 位保护模式的出现,催生了多任务操作系统的诞生,随之而来的就是多线程环境。随着多核时代的到来,多线程环境成为程序开发不可逃避的问题。无锁编程,并行编程等已经成熟的技术在 C 社区里,常常给初学者遥不可及的感觉。在迫切需要多线程的时代,不同厂商和平台都纷纷开发了各自的多线程库,如 POSIX 的 pthreads 和 window 的 winThread 。软件工程师在特定的平台下,使用各自的线程库来开发多线程或并发程序,并非很难,但可移植却成了他们面临的问题。如 pthreads 在默认情况下,互斥锁是非递归的,要使用递归互斥锁,程序库必须支持一些扩展 feature ;而 window 下的锁默认情况下是递归的。 lock-wait-wakup 方式也不尽然相同,这对开发跨平台的多线程程序来说无异于雪上加霜。
放眼 C 语言的后来者,如 Java ,很早就支持了多线程,并且它的内存模型在不断地修改,以适应发挥多线程的优势。 Erlang 作为一种天生的并发编程语言,踏上编程语言的行列,成为并发编程的新星。同时,很多脚本语都内置地支持线程类或结构。 C/C++ 作为后来加入到多线程的行列,尽管已经太晚了,但对于 C 语言社区来说,这无疑是最令人兴奋的消息了。
软件事务内存导论(五)创建嵌套事务
声明:本文是《Java虚拟机并发编程》的第六章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。
1.1 创建嵌套事务
在之前的示例中,每个用到事务的方法都是各自在其内部单独创建事务,并且事务所涉及的变动也都是各自独立提交的。但如果我们想要将多个方法里的事务调整成一个统一的原子操作的时候,上述做法就无能为力了,所以我们需要使用嵌套事务来实现这一目标。
通过使用嵌套事务,所有被主控函数调用的那些函数所创建的事务都会默认被整合到主控函数的事务中。除此之外,Akka/Multiverse还提供了很多其他配置选项,如新隔离事务(new isolated transactions)等。总之,使用了嵌套事务之后,只有位于最外层的主控函数事务提交时,其内部所做的变更才会被提交。在具体使用时,为了保证所有嵌套事务能够作为一个整体成功完成,我们需要保证所有函数都必须在一个可配置的超时范围内做完。
阅读全文
为代码签名,供后人瞻仰或唾弃,你敢吗?
如何衡量代码质量的好坏,是否有一个标准,是否可以量化?
我认为答案是否定的。如果今年中央给各省下个死命令,要求年度GDP增长达到10%,我相信每个省一定都能完成任务。这几年,GDP增长都在8%以上,CPI增长不到4%,民族复兴完成了62%,这些都量化的,你是否满意?
回到开发的问题上来,有一些数字,比如bug的个数,reopen的次数能说明一定的问题,但不是全部。它只能描述系统的外在质量的一部分,这个外在质量可以由QA来保证。但是内部质量只能靠开发自己来保证,牺牲内部质量来保证功能和外在质量是不应该的。 阅读全文
软件事务内存导论(四)创建事务
声明:本文是《Java虚拟机并发编程》的第六章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。
创建事务
我们创建事务的目的是为了协调针对多个托管引用的变更。事务将会保证这些变更是原子的,也就是说,所有的托管引用要么全部被提交要么全部被丢弃,所以在事务之外我们将不会看到有任何局部变更(partial changes)出现。此外,我们也可以用创建事务的方式来解决对单个ref先读后写所引发的相关问题。
Akka是用Scala开发出来的,所以如果我们工作中用的是Scala的话,就可以直接幸福地享用Akka简洁明了的API了。对于那些日常工作中不能使用Scala开发的程序员,Akka同样也提供了一组方便的API,以帮助他们通过Java语言来使用该类库的功能。本节我们将会看到如何利用Akka在Java和Scala中创建事务。
首先我们需要选一个适合用事务来解决的例子。我们在第5章中重构的EnergySource类使用了显式的加锁和解锁操作(其最终版本详见5.7节),下面让就我们将这些显式的加锁/解锁操作换用Akka的事务API来实现。
软件事务内存导论(三)用Akka/Multiverse STM实现并发
声明:本文是《Java虚拟机并发编程》的第六章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。
用Akka/Multiverse STM实现并发
上面我们已经学习了如何在Clojure里使用STM,我猜你现在一定很好奇如何在Java代码中使用STM。而对于这一需求,我们有如下选择:
- 直接在Java中使用Clojure STM。方法非常简单,我们只需将事务的代码封装在一个Callable接口的实现中就行了,详情请参见第7章。
- 喜欢用注解(annotation)的开发者可能会更倾向于使用Multiverse的STM API.
- 除了STM之外,如果我们计划使用角色(actor),那么还可以考虑选择Akka库。
Multiverse是由Peter Veentjer主持开发的一个基于Java的STM实现。通过这个库,我们可以在Java代码中使用注解来标识事务边界。我们既可以用@TransactionalMethod注解将单个的方法标记为事务性的,也可以用@TransactionalObject注解将一个类的所有方法都标记为事务性的。为了与其他JVM上的语言进行集成,Multiverse还提供了一组丰富的API来控制事物的开始和结束。
Akka是一个由Jonas Boner主持开发的一个基于Scala的解决方案,该方案可以用于包括Java在内的很多其他运行于JVM上的语言。Akka不但提供了STM和基于角色(actor)的并发方案,还提供了将二者混合使用的选项。此外,Akka使用Multiverse作为其STM的实现并提供了ACI(ACID的子集)特性。
Akka的性能非常棒,并且由于它既支持STM又支持基于角色(actor)的模型(详情请参见第8章),本章我们将会用它来实现演示Java STM的例子。
软件事务内存导论(二)软件事务内存
声明:本文是《Java虚拟机并发编程》的第六章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。
1.1 软件事务内存
将实体与状态分离的做法有助于STM(软件事务内存)解决与同步相关的两大主要问题:跨越内存栅栏和避免竞争条件。让我们先来看一下在Clojure上下文中的STM是什么样子,然后再在Java里面使用它。
通过将对内存的访问封装在事务(transactions)中,Clojure消除了内存同步过程中我们易犯的那些错误(见《Programming Clojure》[Hal09]和《The Joy of Clojure》[FH11])。Clojure会敏锐地观察和协调线程的所有活动。如果没有任何冲突——例如,每个线程都在存取不同账户——则整个过程中就不会涉及到任何锁,于是也就不会有延迟,并最终达到最大的并发度。当有两个线程试图访问相同数据时,事务管理器就会介入解决冲突,而我们的代码也就无需涉及任何显式加锁的操作。下面让我们一起研究一下这套系统是如何运作的。
在设计上,值(values)是不可变的,而实体(identities)也仅在Clojure的事务中是可变的。在Clojure中,压根就没有改变状态的方法,也没有任何相关的编程工具可用。而如果出现任何试图在事务之外改变实体的动作时,系统就会抛出illegalStateException异常。换句话说,一旦与事务进行了绑定,在没有冲突时,所有变更都是即时生效的;而一旦发生冲突,Clojure将会自动将事务回滚并重试。我们程序员的主要职责是保证事务中的代码都是幂等的——这是我们在函数式编程中避免副作用的常用手段,而这种手段在Clojure的编程模型中也同样适用。 阅读全文
软件事务内存导论(一)前言
声明:本文是《Java虚拟机并发编程》的第六章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。
请回忆一下你最近完成那个需要对共享可变变量进行同步的项目。在那个项目中,你肯定无法身心愉悦地享受出色地完成工作所带来的乐趣,而是会陷入无尽的质疑之中并抓狂地挨个确认是否在所有需要的地方都作了适当的同步。在过去所经历过的编程工作中,我已经遇到过好几次这样令人神经衰弱的情况了,而其中绝大部分原因都是由于我们在用Java编程的时候没有遵循正确原则和方法来处理共享可变状态。如果我们在某个该同步的地方忘了进行同步,那些不可预知的、潜在的灾难性的结果就将在不远处等待着我们。但是人无完人,遗忘是我们的天性。所以我们应该充分利用工具来弥补我们自身的不足,同时也可以让工具帮助我们实现我们充满创意的大脑所追求的那些伟大的目标,而不是让错误一次次地打击我们的信心。为了能够得到可控的行为和结果,我们需要再次把目光投向JDK。
在本章中,我们将会通过使用Clojure中十分流行的软件事务内存(STM)模型来学习如何线程安全地处理共享可变性(shared mutability)。在需要的时候,我们可能会在示例项目中混入Clojure的代码。但是我们并非强迫你也要使用Clojure,因为随着Multiverse和Akka这些优秀工具的出现,我们也可以在Java中直接使用STM了。在本章中,我们会先来看看STM在Clojure里是什么样子,然后再学习如何用Java和Scala对事务内存进行编程。这种编程模型非常适用于那些读多写少的程序——它简单易用并能提供可预测的结果。 阅读全文
并发编程网站推荐
并发编程网站
- Thread newsgroup(很多多线程相关的问题)
- preshing
- Doug Lea workstation (并发编程大师Doug lea的个人网站)
- Concurrency mail list (Doug lea搞的邮件列表)
- oracle dave (oracle 并发编程小组成员)
- lycog.com
- edu papers (很多并发编程相关的论文)
- oracle concurrency (oracle网站)
- mechanical-sympathy(需要翻墙,主要分享如何利用硬件的特性来实现高性能编程)
- Quora concurrency (quora问答网站的并发问题讨论)
- stackoverflow concurrency(stackoverflow问答网站的并发问题讨论)
- developerworks concurrency(并发编程专题文章)
- tumblr concurrency (涵盖多个语言的并发相关文章)
- JDK concurrent(官方jdk1.6 concurrent包API中文文档)
- trans-memory (研究内存事务的网站)
Linux内核的内存屏障
原文链接 作者:David Howells、Paul E. McKenney 译者:曹姚君 校对:丁一
内容: