惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Forbes - Security
Forbes - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Palo Alto Networks Blog
Martin Fowler
Martin Fowler
T
Threatpost
D
Docker
S
Schneier on Security
M
MIT News - Artificial intelligence
G
Google Developers Blog
L
LINUX DO - 热门话题
J
Java Code Geeks
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
博客园 - Franky
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
Google DeepMind News
Google DeepMind News
N
News and Events Feed by Topic
V
Vulnerabilities – Threatpost
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
人人都是产品经理
人人都是产品经理
Spread Privacy
Spread Privacy
T
Tailwind CSS Blog
爱范儿
爱范儿
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42
C
CERT Recently Published Vulnerability Notes
The GitHub Blog
The GitHub Blog
Simon Willison's Weblog
Simon Willison's Weblog
NISL@THU
NISL@THU
MongoDB | Blog
MongoDB | Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Heimdal Security Blog
Recorded Future
Recorded Future
云风的 BLOG
云风的 BLOG
SecWiki News
SecWiki News
P
Privacy International News Feed
P
Proofpoint News Feed
O
OpenAI News
B
Blog
腾讯CDC
F
Full Disclosure
Apple Machine Learning Research
Apple Machine Learning Research
T
Tor Project blog
H
Hacker News: Front Page
Project Zero
Project Zero
Hugging Face - Blog
Hugging Face - Blog
C
Cisco Blogs
S
Security Affairs

Parallel Labs

Architect和Artisan - Parallel Labs 创业与企业家精神 - Parallel Labs 采访Hadoop创始人Doug Cutting纪要 - Parallel Labs 智能优化&AB测试-实验驱动用户增长@QCon10 PPT分享 - Parallel Labs Druid 6th Meetup资料下载 - Parallel Labs 增长二三事 - Parallel Labs 两个平行世界 - Parallel Labs Shape the world to come - Parallel Labs 2018新年目标 - Parallel Labs 人工智能芯片公司招聘工程师/行政/出纳 - Parallel Labs Druid中国用户组第一次线下技术交流资料分享 - Parallel Labs 再见了,IBM中国研究院 | Parallel Labs 怎样做颠覆式创新? - Parallel Labs 基于OpenStack, Docker和Spark打造SuperVessel大数据公有云 - Parallel Labs 给Vim配置Scala语法高亮显示 - Parallel Labs 一步一步教你怎样给Apache Spark贡献代码 - Parallel Labs 大数据的价值密度 - Parallel Labs IBM研究院(CRL)诚聘 Bigdata/Clould 方向正式员工 - Parallel Labs My Way - Parallel Labs Impala:新一代开源大数据分析引擎 - Parallel Labs Impala与Stinger对比 - Parallel Labs Git快速学习指南 - Parallel Labs 与Google拼音的工程师聊聊中文滑行输入 - Parallel Labs 仰望星空 脚踏实地 - Parallel Labs 记一次诡异的Debug经历 - Parallel Labs 下一代大数据分析技术 - Parallel Labs 多核与异步并行 - Parallel Labs 做好失败的准备 - Parallel Labs Facebook技术分享: Social Networking at Scale - Parallel Labs 为什么NoSQL和Hadoop该一起使用? - Parallel Labs Understanding System and Architecture for Big Data - Parallel Labs C++ AMP异构并行编程解析 - Parallel Labs Intel Nehalem微处理器架构 by Glenn Hinton (Intel Fellow) - Parallel Labs 云计算时代的多核开发 - Parallel Labs X-RIME: 基于Hadoop的开源大规模社交网络分析工具 - Parallel Labs 并行编程中的“锁”难题 - Parallel Labs [已经招到了,谢谢大家!]IBM中国研究院招聘Hadoop实习生 - Parallel Labs IBM中国研究院招聘大规模数据分析实习生 - Parallel Labs 浅析C++多线程内存模型 - Parallel Labs Facebook的Realtime Hadoop及其应用 - Parallel Labs 你好,2011! - Parallel Labs 移动设备进入多核时代! - Parallel Labs 为什么在多核多线程程序中要慎用volatile关键字? | Parallel Labs Jeff Dean关于Google系统架构的讲座 - Parallel Labs Erlang User Conference 2010见闻(兼谈程序员职业生涯) - Parallel Labs 多线程程序常见Bug剖析(下) - Parallel Labs 多线程程序常见Bug剖析(上) - Parallel Labs 史蒂夫乔布斯(Steve Jobs)在Stanford2005年毕业典礼上的演讲 - Parallel Labs 多线程队列的算法优化 - Parallel Labs Google创始人的求职目标 - Parallel Labs 多核的未来 - Parallel Labs 多核编程的难题(二) - Parallel Labs 多核编程的难题(一) - Parallel Labs 二进制的二三事 - Parallel Labs 聊一聊瑞典的程序员 - Parallel Labs 多线程程序中操作的原子性 - Parallel Labs 第三次软件危机 - Parallel Labs 实施并行编程的五大障碍 - Parallel Labs 为什么程序员需要关心顺序一致性(Sequential Consistency)而不是Cache一致性(Cache Coherence?) | Parallel Labs 八条设计多线程程序的简单规则 - Parallel Labs 瑞典Ericsson总部Master Thesis面试回忆录 | Parallel Labs Pthreads并行编程: 线程同步之spin lock与mutex性能比较 | Parallel Labs How to do performance analysis on your parallelized program efficiently? - Parallel Labs 09年感悟 - Parallel Labs Proposal for the “Search and sort” competition of Findwise - Parallel Labs 在瑞典打甲流疫苗 - Parallel Labs The Longest Plateau | Parallel Labs Launched my master thesis finally - Parallel Labs Hello world! - Parallel Labs
《程序员的自我修养》中关于加锁不能保证线程安全的一个错误 - Parallel Labs
Guancheng (G.C.) · 2011-04-09 · via Parallel Labs

在《程序员的自我修养 — 链接装载与库》一书第28页“过度优化”这一节中,作者提到了编译器优化可能造成多线程bug的情况(我手中的是09年6月第二次印刷那版)。原文如下:

线程安全是一个非常烫手的山芋,因为即使合理的使用了锁,也不一定能保证线程安全,这是源于落后的编译器技术已经无法满足日益增长的并发需求。很多看似无错的代码在优化和并发前又产生了麻烦。最简单的例子,让我们看看如下代码:

x = 0;
Thread 1 Thread 2
lock(); lock();
x++; x++;
unlock(); unlock();

由于有lock和unlock的保护,x++的行为不会被并发所破坏,那么x的值似乎必然是2了。然后,如果编译器为了提高x的访问速度,把x放到了某个寄存器里,那么我们知道不同线程的寄存器是各自独立的,因此如果Thread 1先获得锁,则程序的执行可能会呈现如下的执行情况:

*1 Thread 1:读取x的值到某个寄存器R[1] (R[1]=0)
*2 Thread 1:R[1]++
*3 Thread 2:读取x的值到某个寄存器R[2] (R[2]=0)
*4 Thread 2:R[2]++
*5 Thread 2:将R[2]写回至x(x=1)
*6 Thread 1:(很久以后)将R[1]写回至x(x=1)

可见在这样的情况下即使正确的加锁,也不能保证多线程安全。

这个“加锁后仍不能保证线程安全”的结论其实是错误的。在对一段代码进行加锁操作之后,被锁保护起来的代码就形成了一个临界区,在任何时刻最多只能有一个线程运行这个临界区中的代码,而其他的线程必须等待(例如pthread_mutex_lock是阻塞型等待,pthread_spin_lock是忙等待)。给临界区加锁之后相当于给临界区内的代码添加了原子性的语义。

既然加锁之后临界区内的代码是原子操作的,那么就不可能出现《程》中描述的那种执行顺序,因为Thread 2必须要等到Thread 1执行完x++和unlock()之后才能获得锁并随即进行x++操作。即如下所述的执行顺序:

*1 Thread 1:lock()
*2 Thread 1:读取x的值到某个寄存器R[1] (R[1]=0)
*3 Thread 1:R[1]++
*4 Thread 1:将R[1]写回至x(x=1)
*5 Thread 1:unlock()
*6 Thread 2:lock() //得到锁
*7 Thread 2:读取x的值到某个寄存器R[2] (R[2]=1)
*8 Thread 2:R[2]++
*9 Thread 2:将R[2]写回至x(x=2)
*10 Thread 2:unlock()

其实,这里更值得讨论的一个问题是memory visibility(内存可见性)。例如,在Thread 1将R[1]的值写回至x的这一步中,如果Thread 1只是将值放到了这个CPU核的write buffer(write buffer是多核CPU中为于优化写性能的一种硬件)里,而未将最新值直接更新至内存,那么处在另一个CPU核上的Thread 2真的有可能在第7步时读到的是x的旧值0,这下该怎么办?这个问题其实就是共享变量的值何时能被其他线程可见的问题。

好在正是因为内存可见性在共享内存的并行编程中如此的重要,所以以pthread为代表的线程库早就规定好了自己的内存模型,其中就包括了memory visibility的定义:

Memory Visibility
– When will changes of shared data be visible to other threads?
– Pthreads standard guarantees basic memory visibility rules
» thread creation
• memory state before calling pthread_create(…) is visible to created thread
» mutex unlocking (also combined with condition variables)
• memory state before unlocking a mutex is visible to thread which locks same mutex
» thread termination (i.e. entering state “terminated”)
• memory state before termination is visible to thread which joins with terminated thread
» condition variables
• memory state before notifying waiting threads is visible to woke up threads

说简单点,Pthreads线程库帮程序员保证了pthread mutex(spin lock也一样)所保护的临界区内共享变量的可见性:即Thread 1一执行完unlock(),x的最新值1一定能被Thread 2看见。(为了实现这一点,Pthreads线程库在实现的时候都会根据相应的硬件平台调用相应的memory barrier来保证内存可见性,感兴趣的同学可以看看nptl的实现)

所以,只要正确的用锁保护好你的共享变量,你的程序就会是线程安全的。《程》中所给出的上述例子其实是错误的。

PS.《程》确实是本好书,作者作为我的同龄人功力还是令人钦佩的。但是这个例子也反映了一个现实:写书最怕的就是出现重大的原则性错误,而博客作为互联网上的公开资源,能更容易的吸收大家的修改意见,保证文章的正确性。

参考文献:
[1] Programming with POSIX Threads
[2] Mutex and Memory Visibility