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

来源:互联网 发布:神魔诛星轮进价数据 编辑:程序博客网 时间:2024/06/08 00:34

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


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

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

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

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

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

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

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

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

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


原创粉丝点击