注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

为着理想勇敢前进

 
 
 

日志

 
 

怎样写多线程程序  

2007-05-12 06:38:13|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
多线程程序是很容易出错的。使用加锁的代码很容易碰上并发的冲突,比如说把一个资源读了一半的时候被别的线程改了之类的。唯一可以做到不出错的办法就是不加锁,用消息解决,要完全做到不用锁,其充分必要条件就是所有线程之间零共享数据。
任何代码都会出错,这里说的多线程程序不出错的含义就是,如果一个bug在这个多线程程序中出现,那么这个bug也同样会在单线程程序中出现。
具体的解决办法是把所有异步逻辑的控制都放到主线程,而把耗时操作放到别的线程,这些耗时操作对于主线程来说就是一个异步调用,不占用主线程的CPU。而别的专门处理耗时操作的线程的逻辑非常简单,需要知道的数据也只有那一个操作相关的数据,所以就可以零共享数据。耶~~~

异步调用是什么含义呢?通常是主线程post一个请求给耗时操作的线程,post所带的信息包括回调函数以及其他请求的参数。耗时操作的线程把这个耗时操作完成以后就把对应的回调函数post给主线程执行。强调,所有线程间通信都是post,post非常快,而且在上层不必考虑同步问题。对于erlang 这样的专用的并发语言,这是线程间通讯的唯一方式。

但是很多现有的程序是没办法写成那样的,因为单件类就是最常见的共享数据。然而,大量的现有代码依赖于单件类,这是非常恶劣的。想要做到零共享数据,必须不使用任何单件类!

那么,那些你们看起来似乎是理所当然应该用单件类的时候应该怎么解决呢?这就引出另一个问题了,那就是,对于一个模块来说,它所需要知道的信息应该是越少越好,和外部的接口越简单越好,它需要用到的这一点点信息,应该是模块的使用者传递进来的,而模块不会把它保存在一个全局变量或者任何单件类里面,这方面的一个典型例子就是所有Lua API的那个Lua_State的参数。

我前面所说的,用于执行耗时操作的线程,其相关数据也封装成这样一个模块,模块初始化只需要很少的信息,每一次异步请求也都只需要很少的信息,而这些很少的信息都是外界传进来的,而不会保存在全局变量里面,也没有其他依赖。那么,它就是一个干净的模块,一个可重用的模块。

另外一些看起来应该要放到单件类里的东西则应该放到主逻辑的入口函数里面,作为其中的局部变量。一个难点就在于主逻辑的代码会很快膨胀,这个入口函数中的一些结构会膨胀的看起来就像是全局变量的温床,而且这些结构会传递给各个不同的模块到处使用。

唯一的解决办法是重构。把主逻辑中,已经变长了的代码划分出来,划分成不同的模块。划分的标准是数据,经常访问某一数据的代码就应该划分成一个模块,而这些数据专门有这个模块来管理。同样的保持上面的“干净”的原则,尽量保持每一个模块所需要的依赖最少,用到的数据最少,功能最简单。要保持每一个模块干净,往往还有一些别的要求,比如说正交性,两个模块之间不应该有重复的功能。

归根到底就是简单二字,保持任何代码都最简单,不是一件简单的事情。
  评论这张
 
阅读(1550)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018