专于:http://blog.csdn.net/lidatgb/article/details/8363035

结合上篇《多线程的基础》,这次我们写一个多线程的赛跑实例,内容很简单:超人和蜘蛛侠赛跑,因为超人飞的比蜘蛛侠跳的快,为了公平,我们让蜘蛛侠跑的长度小点,裁判负责宣布比赛的开始和结束。

  1. class MultiThread
  2. {
  3. //定义两个线程,分别为超人和蜘蛛侠
  4. private static Thread SuperMan;
  5. private static Thread SpiderMan;
  6. //程序入口,比赛开始
  7. static void Main(string[] args)
  8. {
  9. //初始化数据
  10. InitData();
  11. //裁判吹哨,开始赛跑
  12. JudgeWork();
  13. }
  14. /// <summary>
  15. /// 初始化超人和蜘蛛侠的线程和姓名
  16. /// </summary>
  17. private static void InitData()
  18. {
  19. SuperMan = new Thread(new ParameterizedThreadStart(RunnerWork));
  20. SpiderMan = new Thread(new ParameterizedThreadStart(RunnerWork));
  21. SuperMan.Name = "SuperMan";
  22. SpiderMan.Name = "SpiderMan";
  23. }
  24. /// <summary>
  25. /// 裁判开始比赛,最后宣布胜者
  26. /// </summary>
  27. private static void JudgeWork()
  28. {
  29. Console.WriteLine("{0}   PK   {1}", SuperMan.Name, SpiderMan.Name);
  30. Console.WriteLine("比赛即将开始,请各位做好准备!");
  31. Console.WriteLine("预备!");
  32. Console.Read();
  33. //Superman起跑
  34. Console.WriteLine("回车枪响,Superman开始起跑!");
  35. Console.Beep(654, 1200);
  36. SuperMan.Start(500);
  37. //Monster起跑
  38. Console.WriteLine("回车枪响,SpiderMan开始起跑!");
  39. SpiderMan.Start(200);
  40. SuperMan.Join();
  41. SpiderMan.Join();
  42. //宣布赛跑结果
  43. Console.WriteLine("我宣布比赛结束");
  44. //程序暂停12秒
  45. Thread.Sleep(12000);
  46. }
  47. /// <summary>
  48. /// 赛跑的过程
  49. /// </summary>
  50. /// <param name="obj">赛跑参数</param>
  51. private static void RunnerWork(Object obj)
  52. {
  53. int length = Int32.Parse(obj.ToString());
  54. Thread CurrentThread = Thread.CurrentThread;
  55. string CurThreadName = CurrentThread.Name;
  56. int speed;
  57. //超人速度为20
  58. if (CurThreadName == SuperMan.Name)
  59. {
  60. speed = 50;
  61. }
  62. //蜘蛛侠速度为20
  63. else if (CurThreadName == SpiderMan.Name)
  64. {
  65. speed = 20;
  66. }
  67. //如果不可控线程进入,采用以下速度
  68. else
  69. {
  70. speed = 1;
  71. }
  72. Console.WriteLine("{0},开始起跑…………", CurThreadName);
  73. for (int count = speed; count <= length; count += speed)
  74. {
  75. Thread.Sleep(1000);
  76. Console.WriteLine("{0}……跑到了第{1}米", CurThreadName, count.ToString());
  77. }
  78. Console.WriteLine("{0},到达终点!了咧欢迎……", CurThreadName);
  79. }
  80. }

运行结果:

比赛刚刚开始,裁判即宣布结束,这不符合常理。仔细分析可以发现,程序可控制的进程一共有三个,即裁判、超人和蜘蛛侠,三个进程相互独立同时进行,所以裁判宣布比赛开始后即按照它的线程继续宣布结束。
        我们可以这样:在裁判宣布比赛开始后,让蜘蛛侠和超人的线程执行完毕再执行裁判进程:

  1. //防止裁判的主进程先结束,让超人和蜘蛛侠的进程先执行完毕
  2. SuperMan.Join();
  3. SpiderMan.Join();
  4. Console.WriteLine("我宣布比赛结束");

这次的执行结果为:

赛跑结束,裁判才宣布比赛结束,但是还有问题,裁判总得宣布谁跑赢了吧,台底下这么多粉丝等着呢?这个我们可以用变量的方式保存署名,达到宣布谁为冠军的功能。
        为了展示同步异步读写问题,我们让超人赛跑中去拯救世界,然后回来继续比赛;先到达终点的人,自己花时间找粉笔,然后在黑板上署名,其他人看到黑板上有名字就不能再写,裁判宣布署名的人为胜者。

  1. class MultiThread3
  2. {
  3. //署名用的黑板
  4. static string NameBoard = "";
  5. //定义两个线程,分别为超人和蜘蛛侠
  6. private static Thread SuperMan;
  7. private static Thread SpiderMan;
  8. //程序入口,比赛开始
  9. static void Main(string[] args)
  10. {
  11. //初始化数据
  12. InitData();
  13. //裁判吹哨,开始赛跑
  14. JudgeWork();
  15. }
  16. /// <summary>
  17. /// 初始化超人和蜘蛛侠的线程和姓名
  18. /// </summary>
  19. private static void InitData()
  20. {
  21. SuperMan = new Thread(new ParameterizedThreadStart(RunnerWork));
  22. SpiderMan = new Thread(new ParameterizedThreadStart(RunnerWork));
  23. SuperMan.Name = "SuperMan";
  24. SpiderMan.Name = "SpiderMan";
  25. }
  26. /// <summary>
  27. /// 裁判开始比赛,最后宣布胜者
  28. /// </summary>
  29. private static void JudgeWork()
  30. {
  31. Console.WriteLine("{0}   PK   {1}", SuperMan.Name, SpiderMan.Name);
  32. Console.WriteLine("比赛即将开始,请各位做好准备!");
  33. Console.WriteLine("预备!");
  34. Console.Read();
  35. //Superman起跑
  36. Console.WriteLine("回车枪响,SuperMan开始起跑!");
  37. Console.Beep(654, 1200);
  38. SuperMan.Start(500);
  39. //Monster起跑
  40. Console.WriteLine("回车枪响,SpiderMan开始起跑!");
  41. SpiderMan.Start(300);
  42. //防止裁判的主进程先结束,让超人和蜘蛛侠的进程先执行完毕
  43. SuperMan.Join();
  44. SpiderMan.Join();
  45. //宣布赛跑结果
  46. AnnounceWinner();
  47. //程序暂停12秒
  48. Thread.Sleep(12000);
  49. }
  50. /// <summary>
  51. /// 赛跑的过程
  52. /// </summary>
  53. /// <param name="obj">赛跑参数</param>
  54. private static void RunnerWork(Object obj)
  55. {
  56. int length = Int32.Parse(obj.ToString());
  57. Thread CurrentThread = Thread.CurrentThread;
  58. string CurThreadName = CurrentThread.Name;
  59. int speed;
  60. //超人速度为20
  61. if (CurThreadName == SuperMan.Name)
  62. {
  63. speed = 50;
  64. }
  65. //蜘蛛侠速度为20
  66. else if (CurThreadName == SpiderMan.Name)
  67. {
  68. speed = 20;
  69. }
  70. //如果不可控线程进入,采用以下速度
  71. else
  72. {
  73. speed = 1;
  74. }
  75. Console.WriteLine("{0},开始起跑…………", CurThreadName);
  76. for (int count = speed; count <= length; count += speed)
  77. {
  78. Thread.Sleep(1000);
  79. Console.WriteLine("{0}……跑到了第{1}米", CurThreadName, count.ToString());
  80. //超人跑到一半,去拯救世界
  81. if (count == length / 2)
  82. {
  83. if (CurThreadName == SuperMan.Name)
  84. {
  85. Console.WriteLine("世界末日来临,超人去拯救世界……");
  86. string waitInfo = "..";
  87. //超人拯救世界过程
  88. for (int j = 0; j <= 10; j++)
  89. {
  90. Console.WriteLine("超人拯救世界中" + waitInfo);
  91. waitInfo += "..";
  92. Thread.Sleep(1000);
  93. }
  94. Console.WriteLine("超人去拯救世界归来,继续赛跑……");
  95. }
  96. }
  97. }
  98. Console.WriteLine("{0},到达终点!乐咧欢迎……", CurThreadName);
  99. WriteName(CurThreadName);
  100. }
  101. /// <summary>
  102. /// 跑到重点线后,选手自己在黑板上署名
  103. /// </summary>
  104. /// <param name="name">选手姓名</param>
  105. private static void WriteName(string name)
  106. {
  107. //黑板上没名字,才可以署自己的名字
  108. if (NameBoard.Length == 0)
  109. {
  110. Console.WriteLine("{0}去找粉笔了……", name);
  111. //找粉笔花费的时间
  112. Thread.Sleep(9000);
  113. Console.WriteLine("{0}拿着粉笔回来了,开始署名……", name);
  114. NameBoard = name;
  115. Console.WriteLine("{0}署完名后,开心的离开了……", name);
  116. }
  117. //黑板上有署名时不能再署名
  118. else
  119. {
  120. Console.WriteLine("{0}发现已经署名,桑心的离开了……", name);
  121. }
  122. }
  123. /// <summary>
  124. /// 宣布比赛结果
  125. /// </summary>
  126. private static void AnnounceWinner()
  127. {
  128. Console.WriteLine("我是裁判,我宣布这次比赛的冠军是{0}", NameBoard);
  129. }
  130. }

运行结果:


        可以看到明明是SuperMan还在拯救地球时,SpiderMan已经到达终点,而裁判宣布的冠军却是SuperMan。仔细分析一下程序即可知道:虽然SpiderMan先到达终点,并且先发现黑板是空的,但是在SpiderMan寻找粉笔的过程中,SuperMan到达终点,并且也发现黑板是空的,于是两人都写上了自己的名字,但是因为后者的会覆盖前者的,所以胜利者成了SuperMan,整个过程如下图所示:

问题出现的原因在于,SpiderMan到达以后看到黑板,SuperMan仍然看到黑板,即这个黑板对于两个人都是可写的,后者会覆盖前者的内容,这种方式为异步写。
        怎么克服这个问题?可以使用Lock锁住临界区代码,如下:

  1. //定义一个对象类型的objLock
  2. private static object objLock = new object();
  3. /// <summary>
  4. /// 跑到重点线后,选手自己在黑板上署名
  5. /// </summary>
  6. /// <param name="name">选手姓名</param>
  7. private static void WriteName(string name)
  8. {
  9. //采用异步读方式,筛选掉已经看到署名的线程,提高效率
  10. //黑板上没名字,才可以署自己的名字
  11. if (NameBoard.Length == 0)
  12. {
  13. //因为上面为异步读,所以可能多个线程可以进入到这一步
  14. lock (objLock)
  15. {
  16. //同步读方式
  17. if (NameBoard.Length == 0)
  18. {
  19. Console.WriteLine("{0}去找粉笔了……", name);
  20. //找粉笔花费的时间
  21. Thread.Sleep(9000);
  22. Console.WriteLine("{0}拿着粉笔回来了,开始署名……", name);
  23. NameBoard = name;
  24. Console.WriteLine("{0}署完名后,开心地离开了……", name);
  25. }
  26. //黑板上有署名时不能再署名
  27. else
  28. {
  29. Console.WriteLine("{0}发现已经署名,桑心地离开了……", name);
  30. }
  31. }
  32. }
  33. }

需要注意的是,锁住的内容(非临界区代码)必须是共享型的引用型数据,因为如果是局部变量针对一个线程锁不锁对其它线程意义不大;采用引用数据类型,可以保证每个线程锁住内容都指向同一个地址。

为了直观显示,没有抽象出超人、蜘蛛侠和裁判的类,以上就是一个简单的多线程应用实例,当然这是多线程的冰山一角,更多的还有待在以后开发中实践,这次争取在考试系统使用多线程优化抽题和判分等功能。

(转) C#多线程赛跑实例的更多相关文章

  1. vc 基于对话框多线程编程实例——线程之间的通信

     vc基于对话框多线程编程实例——线程之间的通信 实例:

  2. C#多线程编程实例 螺纹与窗口交互

    C#多线程编程实例 螺纹与窗口交互 代码: public partial class Form1 : Form { //声明线程数组 Thread[] workThreads = new Thread ...

  3. c# 多线程 创建对象实例

    本次的标题是我在写单例模式的博客时遇到的问题,所以今天专门写了的demo让自己记住怎么简单的使用多线程. 一直纠结的是怎么在for循环中多次实例化对象,好复现单例模式在没有加锁的情况下出现多个实例对象 ...

  4. linux下C语言多线程编程实例

    用一个实例.来学习linux下C语言多线程编程实例. 代码目的:通过创建两个线程来实现对一个数的递加.代码: //包含的头文件 #include <pthread.h> #include ...

  5. Java单线程多实例和多线程多实例

    最近写了一个程序,是采用多线程往redis里面写入数据,想统计一下一共写了多少条数据,于是用了一个static的全局变量count来累加,这块代码抽象出来就是这样的: public class Mul ...

  6. C#多线程编程实例 线程与窗体交互

    C#多线程编程实例 线程与窗体交互 代码: public partial class Form1 : Form { //声明线程数组 Thread[] workThreads = ]; public ...

  7. java多线程编程实例

    [转]这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下. 1.三个售票窗口同时出售20张票程序分析:   ...

  8. Java 多线程 简单实例 (Runnable)

    1.多线程实例 package second; public class A implements Runnable { public char stat = '*'; public void run ...

  9. Java 多线程 简单实例 (Thread)

    package second; public class A extends Thread { public void run(){ for(int i = 1;i <= 10 ; i++){ ...

随机推荐

  1. Java学习笔记之_JDBC

    JDBC简介 1.SUN公司为了简化,统一数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC 2.数据库驱动 3.JDBC全称为:JAVA DataBase Commectivity(j ...

  2. JS 面向对象 编程设计

    面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装.继承.多态的特性!但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义J ...

  3. 六个字符,带你领略JavaScript (js的艺术编写)

    正文从这开始- JavaScript是一门神奇且奇妙的编程语言,我们有时候用它来写一些看似疯狂的代码,但这些代码依然可被执行且运行结果十分有趣.JavaScript 试图帮助我们将一些数据类型转化为我 ...

  4. elasticsearch分词插件的安装

    IK简介 IK Analyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包.从2006年12月推出1.0版开始, IKAnalyzer已经推出了4个大版本.最初,它是以开源项目Luen ...

  5. Mybatis学习记录(六)----Mybatis的高级映射

    1.一对多查询 1.1 需求 查询订单及订单明细的信息. 1.2 sql语句 确定主查询表:订单表 确定关联查询表:订单明细表 在一对一查询基础上添加订单明细表关联即可. SELECT orders. ...

  6. Atitit.office word  excel  ppt pdf 的web在线预览方案与html转换方案 attilax 总结

    Atitit.office word  excel  ppt pdf 的web在线预览方案与html转换方案 attilax 总结 1. office word  excel pdf 的web预览要求 ...

  7. Sharepoint增加修改密码功能

    Sharepoint中没有自带的修改密码的功能. 如果使用的是AD验证,修改密码,只要修改域帐号的用户名密码就可以了.以下代码可以修改本机密码和域帐号密码. 做法是,添加一个webpart,做一个页面 ...

  8. java中判断字符串是否为数字的方法

    一: //1.用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = 0; i < str.length(); ...

  9. 全球最低功耗蓝牙单芯片DA14580的软件体系 -层次架构和BLE消息事件处理过程

    在作者之前发表的<全球最低功耗蓝牙单芯片DA14580的系统架构和应用开发框架分析>.<全球最低功耗蓝牙单芯片DA14580的硬件架构和低功耗>.<全球最低功耗蓝牙单芯片 ...

  10. 蓝牙Ibeacon室内定位和微信摇一摇周边原理分析

    苹果推出Ibeacon室内定位技术是为了弥补GPS无法覆盖室内定位这种场景.苹果意味着创新,在其推动下,蓝牙Ibeacon得到了极大的应用.而腾讯则是利用蓝牙Ibeacon在场景体验方面进行了创新,实 ...