在多线程中如何避免不正确的代码

原创 2012年03月27日 17:01:23

1  避免依赖“线程惯量”
线程总是异步的。当你在可能“稍微同步”的单处理机系统上开发线程代码时,这点尤其重要。没有东西同时在一台单处理机上发生,准备就绪的线程被连续地以相对可知的间隔分时执行。当你在一台单处理机上创建一个新线程时,或唤醒一个在互斥量或在条件变量上等待的线程时,除非比创建它或唤醒它的线程有更高的优先级,否则它不能立刻运行。如果达到了“进程的并发限制”,同样的现象甚至可能在一台多处理机上发生,如:当就绪线程超过处理器数目时,若有相等的优先级,创建线程或唤醒它的其他线程将继续运行直到被堵住或直到下一个时间片。


2  绝对不要假设你创建的线程会等待你
修复inertia.c的一个方法就是在创建线程之前设置After value为你希望线程看到的值。按照线程的内存可视性规则,新线程可以看到所有在pthread_create调用前发生的内存写。因此,总是将你的代码设计成这种方式:在线程需要的资源被创建并恰当地初始化后,才让线程启动。

3  别将你的赌押在线程竞争上
当写多线程代码时,应该假定在任意点上、在程序的任何语句内,每个线程可能睡眠一段不可知的时间。处理器可以以不同的速率执行你的线程,这取决于处理器的负担、中断等。处理器上的时间片机制可以在任意点上将线程打断一段未指定的时间。当一个线程不在运行时,任何其他线程可以运行并且做(代码的同步协议没有具体阻止它做的)任何东西。这意味着对于不同的活跃线程集,线程在两条指令间可能发现一幅完全不同的存储器图像。防止一个线程以奇怪方式看世界的方法只能依靠线程之间的显式同步。

竞争要求并发执行,即使有时间片限制,其并发水平也是相当低的,而且,一些未同步的写操作经常在其他线程有机会读不一致数据之前就完成了。甚至在一台多处理机上,竞争也可能是很难重复的,且它们经常拒绝向调试器提示自己。竞争取决于线程执行的相对时序--这些很可能在调试器中被改变。  竞争更多与内存可视性相关(与多个写操作的同步相比)。内存可视性基本规则:一个线程总是能看到被同一处理器上的前面执行的线程做的存储器变化。

除非你导致排序,线程间不存在顺序。线程将可能以最邪恶的顺序执行。

记住当使用线程编程时,有更多的小东西被放纵。别心存侥幸,别做任何假设。保证任何共享状态在使用它的线程被创建之前就被建立且可见;或使用静态互斥量或pthread_once来创建它。使用一个互斥量保证线程不能读不一致的数据。如果你必须在线程之间共享栈数据,要确定使用数据的所有线程在从分配内存的函数返回前终止。要解决线程不是顺序执行的问题的最好方法是通过设计代码以使线程启动顺序没有关系来避免这个问题。线程越对称,对环境做的假设越少,这种竞争发生的机会越少。
在线程编程时还要注意使用线程可重入的函数或叫做线程安全函数。

4  合作避免僵局
与竞争一样,死锁是一个程序中同步问题的结果。竞争是由于同步不够引起的资源冲突,而死锁通常是有关同步使用的冲突。当任何两个线程共享资源时,死锁可能发生。
死锁的一个常见原因是一些线程没有解锁互斥量就从一个函数中返回。

5  小心优先级倒置
优先级倒置是依赖于实时优先级调试的应用(或库)所独有的一个问题。优先级倒置至少涉及三个具有不同优先级的线程。不同的优先级是重要的---优先级倒置是在同步与调试之间的冲突。优先级倒置允许一个低优先级线程无止境地阻止一个高优先级线程运行。结果通常不是死锁,但是它总是一个严重的问题。
以下是避免优先级倒置的一些想法:
完全避免实时调度。然而,这显然在许多实时应用中是不实际的。
设计你的线程使不同优先级的线程不需要使用同一个互斥量。这可能是不切实际的;如:许多ANSIC函数使用互斥量。
使用优先级ceiling互斥量或优先级继承。这些是pthreads的可选特性并且不是随处可得的。另外,你不能为不是你创建的互斥量设置优先级协议,包括那些由ANSIC函数使用的互斥量。
避免调用这样的函数:它可能锁住不是你创建的互斥量并提升互斥量的优先级。

6  绝不要在谓词之间共享条件变量
如果避免使用单个条件变量来管理多个谓词条件,你的代码通常将更干净和更有效。

7  共享堆栈和相应的内存破坏
在线程间共享堆栈内存没有什么不对的。即 线程在自己的堆栈中分配变量并将地址通知给其他线程是合法的,有时也是合理的。程序编写无误,则共享堆栈地址根本没有危险;然而,并不是每个程序都正确编写,即使你希望它正确时。共享堆栈地址能使小的编程错误成为灾难,而且很难将这种错误隔离。若共享堆栈内存,你必须保证拥有堆栈的线程决不会在其他线程停止使用共享数据之前将共享数据从堆栈中“弹出”。
在线程和共享堆栈中,每个线程有机会破坏由其他线程异步使用的数据。数据破坏的征兆可能直到一段时间后才能显示出来。
在编写的程序中,如果共享堆栈数据是方便的,则一定要利用这个功能。但是如果在调试过程中出现了未预料的事情,你需要特别仔细地检查共享堆栈数据的代码。


多线程死锁的产生以及如何避免死锁

一、死锁的定义 多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进...
  • ls5718
  • ls5718
  • 2016年07月13日 11:07
  • 19303

《java并发编程实战》读书笔记——避免死锁的发生

我们使用加锁机制来确保线程安全,但如果过度地使用加锁,则可能导致锁顺序死锁。当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么他们将永远被阻塞。 在数据库系统的设中考虑了检测死锁以及从...
  • Great_Smile
  • Great_Smile
  • 2015年03月25日 13:50
  • 793

秒杀多线程-读者写者问题

本文转载自秒杀多线程第十一篇 读者写者问题 与上一篇的生产者消费者问题一样,读者写者也是一个非常著名的同步问题。读者写者问题描述非常简单,有多个写者和多个读者,多个读者可以同时读文件,多个写者不可以 ...
  • qq_32400847
  • qq_32400847
  • 2016年11月01日 15:31
  • 195

什么是多线程,锁,死锁,怎么避免死锁

面试官问线程的问题,以前从来没有总结过怎么回答,一下子就说不出来了。回来细细总结一下,下次不能再不知道从哪里说起了   细思之, 首先应该 了解什么是线程、线程是资源分配的基本单位,程序执行流的最小...
  • zouxueleics
  • zouxueleics
  • 2016年03月17日 22:03
  • 1858

再谈Python多线程--避免GIL对性能的影响

GIL是CPython中特有的全局解释器锁(其它实现版本因为有自己线程调度机制,所以没有GIL机制)。本质上讲它就是Python进程中的一把超大锁。这把锁在解释器进程中是全局有效的,它主要锁定Pyth...
  • five3
  • five3
  • 2017年11月17日 17:25
  • 286

多线程(十二)CAS 和ABA问题

在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁。锁机制存在以下问题:(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。(2)一...
  • u012834750
  • u012834750
  • 2017年04月06日 11:38
  • 861

C语言之多线程机制(程序可以同时被执行而不会相互干扰)

接触过linux的人或多或少知道,linux有多线程的机制,也就是说程序可以同时执行,不受干扰,关于这个在我以前的博文里有过类似模拟的时间片轮转程序,跟这个其实是类似的。其实在window上,线程的头...
  • morixinguan
  • morixinguan
  • 2016年03月04日 18:39
  • 8139

多线程是否真的有必要?

相比大家在投简历、面试等等过程中,或多或少会遇到这么一个问题:熟悉掌握多线程开发;谈谈你对多线程的认识。        其实,我有这么一个疑问,那就是多线程真的有必要么?根据我这两年来的项目经验,也...
  • winking324
  • winking324
  • 2014年06月14日 00:09
  • 1393

关于多线程导致数据不一致的情况的思考

这里我要引入一个比较不是很常见的Java 内存模型(JMM java Memory Model),线程基础数据会存放在一个自身对应的线程栈中,如果两个线程需要交互必须要通过共享内存中的变量进行。才能够...
  • zsf5201314z
  • zsf5201314z
  • 2016年11月30日 11:37
  • 1021

Java线程面试题(02) Java线程中如何避免死锁

如何避免Java中的死锁?是多线程编程常见问题之一,在高级别的面试中经常被问及,并带来了大量的后续问题。尽管问题看起来很基本,但是一旦开始深入,大部分开发者都会陷入困境。 面试问题从“什么是死锁?”开...
  • u010096900
  • u010096900
  • 2017年11月21日 14:13
  • 226
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:在多线程中如何避免不正确的代码
举报原因:
原因补充:

(最多只允许输入30个字)