多线程

【回目录】

多线程是程序员面试时常常会面对的问题,对多线程概念的掌握和理解水平,也会被一些老鸟用来衡量一个人的编程实力的重要参考指标。不论是实际工作需要还是为了应付面试,掌握多线程都是程序员职业生涯中一个必须经过的环节。其实当你把“多线程”和你的“职业生涯”联系在一起考虑的时候,就会觉得“多线程”是多么的渺小,对,没有跨越不过的山。不过就算它很渺小,但也有可能改变你的人生轨迹。不用担心,如果你对多线程还不太熟悉,那么我们就一起来看看什么是多线程吧。

跟前几篇的风格一样,我会在开篇的时候举一个现实生活中的例子,通过这个例子来映射一些晦涩枯燥的计算机编程专业知识,在让读者朋友很好地理解理论概念的同时,又避免了阅读教科书时的枯燥感觉。这次我要举的例子是公司。不一定是IT公司,尽量和编程领域远一点儿吧,那就假设是一家搬家公司吧。

假如我们把公司看做是一个进程,那么人就是其中的线程。进程必须得有一个主线程,公司在创业初期往往可能出现一人打天下的现象,但是,至少得有一个人,公司才能运作。公司创业初期,业务还不算太多,往往就是老板一个人身兼数职,一天如果只有1、2趟活儿,应该还是忙得过来的。时间长了,随着业务的发展、口碑地建立,生意越来越兴隆,一个人肯定就忙不过来了。假设一天有5个活儿,老板一个人必须搬完A家才能搬B家,搬到黄昏估计也就搬到C家,D和E家都还在焦急地等待着呢。老板一个人要充当搬运工、司机、业务联系人、法人代表、出纳等众多角色,累死累活公司的规模也上不去,人手不够制约了公司的发展。那么怎么办,很简单,增加人手,用编程的话来说就是“再起个线程”。

我们现在就用代码来描述这样的场景吧,首先,我们准备成立一家搬家公司,于是要准备好将来和客户签的合同书:

   1: public class Contract
   2: {
   3:     public string ID { get; private set; }
   4:     public string From { get; set; }
   5:     public string To { get; set; }
   6:     public decimal Fee { get; set; }
   7:  
   8:     public Contract()
   9:     {
  10:         this.ID = DateTime.Now.ToBinary().ToString().Replace("-", String.Empty);
  11:     }
  12: }

 

简是简单了点儿,好歹也是份合同,现在我们就去申请注册一家公司,并组建好初创团队,哪怕目前还只有老板一个人:

   1: public class HouseMovingCompany
   2: {
   3:     private static HouseMovingCompany _instance = null;
   4:     public static HouseMovingCompany Instance
   5:     {
   6:         get { return (_instance == null ? _instance = new HouseMovingCompany() : _instance); }
   7:     }
   8:  
   9:     public List<Contract> Contracts { get; private set; }
  10:  
  11:     public HouseMovingCompany()
  12:     {
  13:         this.Contracts = new List<Contract>();
  14:     }
  15:  
  16:     public void MoveHouse()
  17:     {
  18:         if (this.Contracts == null || this.Contracts.Count == 0)
  19:         {
  20:             return;
  21:         }
  22:  
  23:         Contract contract = contract = this.Contracts[0];
  24:         this.Contracts.RemoveAt(0);
  25:  
  26:         if (!String.IsNullOrEmpty(contract.From) && !String.IsNullOrEmpty(contract.To))
  27:         {
  28:             Console.WriteLine("Move the house from {0} to {1}.", contract.From, contract.To);
  29:         }
  30:  
  31:         Thread.Sleep(5000);
  32:     }
  33: }

好了,现在公司实体有了,老板就可以开始忙活了:

   1: static void Main(string[] args)
   2: {
   3:     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "WuDaokou", To = "LinDa Road", Fee = 500 });
   4:  
   5:     while (HouseMovingCompany.Instance.Contracts.Count > 0)
   6:     {
   7:         HouseMovingCompany.Instance.MoveHouse();
   8:     }
   9: }

我们在前面设置了每次搬家耗时5秒钟,咱们把它想象成5个小时。嗯,一天接一个单子,还可以接受,但是随着老板生意日渐兴隆,有时候一天要接3个单子,这就最少要工作15个小时了,还要操心公司的运营等问题,的确忙不过来了,而且照这样算,老板一天不可能完成5个或5个以上的单子,严重制约了公司的发展:

   1: static void Main(string[] args)
   2: {
   3:     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "WuDaokou", To = "LinDa Road", Fee = 500 });
   4:     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiDan", To = "WangFujing", Fee = 1000 });
   5:     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiangShan", To = "The Forbidden City", Fee = 10000 });
   6:  
   7:     while (HouseMovingCompany.Instance.Contracts.Count > 0)
   8:     {
   9:         HouseMovingCompany.Instance.MoveHouse();
  10:     }
  11: }

一天夜里,老板拖着疲倦的身子回到家里,一进门就一头倒在床上,他极力睁着快睁不开的眼睛,努力地对自己说:“不行,我一定要想个办法,不然我会被累死的!”。

其实办法很简单,谁都知道,招聘几个员工,再买几辆车,大家分头行动,不仅分担了工作负担,而且在规模扩大的同时还可以完成更多更大的单子。好,我们现在就借助多线程机制来实现我们的想法:

   1: static void Main(string[] args)
   2: {
   3:     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "WuDaokou", To = "LinDa Road", Fee = 500 });
   4:     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiDan", To = "WangFujing", Fee = 1000 });
   5:     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiangShan", To = "The Forbidden City", Fee = 10000 });
   6:  
   7:     Thread thread = null;
   8:  
   9:     while (HouseMovingCompany.Instance.Contracts.Count > 0)
  10:     {
  11:         thread = new Thread(new ThreadStart(HouseMovingCompany.Instance.MoveHouse));
  12:  
  13:         thread.Start();
  14:     }
  15: }

在这段程序中,我们分头行动,让每项搬家任务都由一个小团队去完成,结果我们发现,现在做三个单子的时间跟做一个单子的时间是一样的,提高了效率也扩大了公司规模。但是,既然引入了新的工作机制,我们在公司内部也不得不做一些小小的调整:

   1: public void MoveHouse()
   2: {
   3:     if (this.Contracts == null || this.Contracts.Count == 0)
   4:     {
   5:         return;
   6:     }
   7:  
   8:     Contract contract = null;
   9:  
  10:     lock (this.Contracts)
  11:     {
  12:         contract = this.Contracts[0];
  13:         this.Contracts.RemoveAt(0);
  14:     }
  15:  
  16:     if (!String.IsNullOrEmpty(contract.From) && !String.IsNullOrEmpty(contract.To))
  17:     {
  18:         Console.WriteLine("Move the house from {0} to {1}.", contract.From, contract.To);
  19:     }
  20:  
  21:     Thread.Sleep(5000);
  22: }

调整的只是MoveHouse这个方法内部的一些实现细节。公司接到的单子都保存在Contracts中,所以搬家的时候需要去拿一个单子然后根据单子上的信息来工作。原先我们只有一个线程在操作Contracts,倒也不觉得什么,现在有多个线程都在对Contracts中的元素进行存取,我们不得不提防一些意外发生。这就是在使用多线程的时候常常需要考虑的并发问题,所以我们用了lock关键字,当一个线程要操作Contracts时,它先把Contracts锁起来,其实就是声明一下:“现在我在操作它,你们谁都不要动,等我弄完了再说。”在lock块结束时被锁定的对象才会被解锁,其它的线程现在才可以去操作它。

有了多线程机制,你会发现程序可以在更短的时间内完成更多的事情。本文没有将多线程机制中的所有概念面面俱到地列举出来,但是已经向你展示了该如何使用多线程以及什么时候可以考虑使用多线程,其它的一些细节有待你去进一步探索,例如,你可以设置线程的优先级(假设逻辑上跟Fee挂钩,类似于‘加急’)等等。

掌握多线程机制,并让它使你的应用程序变得更加强悍吧。

House Moving Company

posted @ 2008-06-19 11:18 Autumoon 阅读(4917) 评论(31)  编辑 收藏 网摘 所属分类: 白话C#系列

  回复  引用  查看    
#1楼2008-06-19 11:23 | 王德水      
正在看多线程想查点资料,直接在首页看到,支持
  回复  引用  查看    
#2楼2008-06-19 11:33 | U2U      
这样还不是特别深,深入到内核级的调度机制才叫好玩呢 :-D
  回复  引用  查看    
#3楼2008-06-19 12:27 | Ivan-Yan      
讲的不错~
  回复  引用    
#4楼2008-06-19 12:40 | None9999999[未注册用户]
期待下文。。
  回复  引用  查看    
#5楼2008-06-19 12:49 | winzheng      
这种风格挺好,支持
  回复  引用    
#6楼2008-06-19 14:09 | 一卡多号[未注册用户]
好文章!
  回复  引用  查看    
#7楼2008-06-19 14:11 | 无锋不起浪      
学习了
  回复  引用    
#8楼2008-06-19 14:52 | 夕颜子[未注册用户]
最后这个图片 配套的很好
  回复  引用  查看    
#9楼2008-06-19 14:55 | zdjray      
支持,期待下一篇
  回复  引用  查看    
#10楼2008-06-19 15:04 | bidaas      
@U2U
要都谈内核了,那你学线程编程是怎么学的?坐火箭还是开飞机?这样的文章阅读起来很轻松,也让人容易入门。
楼主的风格不错,期待下文。。

  回复  引用  查看    
#11楼2008-06-19 17:34 | Tony Zhou      
excellent
  回复  引用    
#12楼2008-06-19 18:37 | fuadam[未注册用户]
希望能讲一下多线程中的模型,比如生产消费者,还有哲学家问题什么的
  回复  引用  查看    
#13楼2008-06-19 19:08 | 皇帝的新装      
好。这个问题很难讲清楚。
  回复  引用  查看    
#14楼2008-06-19 20:57 | DQW      
学习了
  回复  引用  查看    
#15楼2008-06-19 21:44 | Jacky Chyi      
建议楼主给代码加点注释...虽然有简洁的语言举例子...但是没有代码的注释还是感觉少点什么...
  回复  引用  查看    
#16楼2008-06-20 09:56 | 江水滔滔      
請問如果我做的是一些列操作呢?
比如包装:
A: 根据 机身条码 ,分配号段
多线程中如和来控制事务呢?号段出错了,该如何重分配呢?
多线程设计到资源锁,效率和单线程有多大的提升
关键是如何控制异样?
请指导!

  回复  引用  查看    
#17楼[楼主]2008-06-20 10:01 | Autumoon      
@江水滔滔

这要根据项目的具体需求去设计了,不是一两句话能讲清楚的。请先熟悉了多线程的使用之后再结合具体需求去设计吧,祝工作愉快!

  回复  引用  查看    
#18楼2008-06-20 10:15 | 江水滔滔      
在一般情况下,我做了测试,有的时候涉及到锁,lock 住了,其实效率也并不是那么高,可能还和CPU有关系吧。
还有就是少数量的时候效率也不是那么高!

就比如:人多了就难管理是不是呢?我以前也做过多线程,到现在还是一知半解!

我用了你的列子做了测试,发现本机的效率还不如单的快,10000条记录



  回复  引用    
#19楼2008-06-20 15:17 | haozi[未注册用户]
讲的生动活泼,不错
  回复  引用    
#20楼2008-06-20 16:56 | lusens[未注册用户]
楼主讲得很形象
  回复  引用  查看    
#21楼2008-06-21 21:42 | 张波sun      
学习
  回复  引用    
#22楼2008-07-01 10:55 | kenzo[未注册用户]
很好用啊。谢谢!
  回复  引用  查看    
#23楼2008-07-01 15:33 | KymoWang      
你是不是为了快速测试,把Thread.Sleep去掉了?CPU只有一个,只有给CPU空闲才能发挥多线程的优势(猜的^o^)
--引用--------------------------------------------------
江水滔滔: 在一般情况下,我做了测试,有的时候涉及到锁,lock 住了,其实效率也并不是那么高,可能还和CPU有关系吧。
还有就是少数量的时候效率也不是那么高!

就比如:人多了就难管理是不是呢?我以前也做过多线程,到现在还是一知半解!

我用了你的列子做了测试,发现本机的效率还不如单的快,10000条记录



--------------------------------------------------------

  回复  引用    
#24楼2008-08-13 19:53 | oscarzq[未注册用户]
楼主对线程很精通,请教个问题,我想用线程调用一个未知(参数和返回类型不确定)的方法,怎么调用阿?谢谢解答。
  回复  引用  查看    
#25楼2008-08-19 08:54 | Eric zhou      
支持
  回复  引用  查看    
#26楼2008-10-15 18:15 | 梦涯      
@oscarzq
我觉得应该用反射

  回复  引用  查看    
#27楼2008-10-15 18:21 | 梦涯      
但是我觉得楼主本意是想加深我们对线程的理解,可是我觉得用这个列子有点不太妥当,首先多线程机制在实际中并不能达到象三个线程同时运行就能省下两个线程的时间,因为毕竟CPU只有一个,并不能说有三个CPU同时运行,所以多线程只能说计算时间片让这些线程轮流运行,不能达到搬家公司3个人同时搬的效果~呵呵 刚接触程序 一点拙见~~
  回复  引用    
#28楼2009-01-30 23:28 | 591624310@qq.com[未注册用户]
什么程序不是靠时间片运行的?开两个QQ你说是同时运行,这和开两个线程有什么不同?只不过一个是进程一个是线程而已
楼上的在说废话,鉴定完毕!
另外,我就恶心线程中的代码不能调试么?每次都是编译器卡一下然后程序就中断了,希望博主能解惑

  回复  引用    
#29楼2009-06-13 15:52 | NeoChina
浅显易懂
很好的文章
楼主辛苦了

  回复  引用    
#30楼2009-06-30 13:41 | reyzhang[未注册用户]
浅显易懂,再讲的深入一点会更好
  回复  引用    
#31楼2009-07-01 13:27 | reyzhang[未注册用户]
while (HouseMovingCompany.Instance.Contracts.Count > 0)
{
thread = new Thread(new ThreadStart(HouseMovingCompany.Instance.MoveHouse));
thread.Start();

}

这貌似是个死循环




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1225684




相关文章:

相关链接: