(转) C#多线程赛跑实例
专于:http://blog.csdn.net/lidatgb/article/details/8363035
结合上篇《多线程的基础》,这次我们写一个多线程的赛跑实例,内容很简单:超人和蜘蛛侠赛跑,因为超人飞的比蜘蛛侠跳的快,为了公平,我们让蜘蛛侠跑的长度小点,裁判负责宣布比赛的开始和结束。
- class MultiThread
- {
- //定义两个线程,分别为超人和蜘蛛侠
- private static Thread SuperMan;
- private static Thread SpiderMan;
- //程序入口,比赛开始
- static void Main(string[] args)
- {
- //初始化数据
- InitData();
- //裁判吹哨,开始赛跑
- JudgeWork();
- }
- /// <summary>
- /// 初始化超人和蜘蛛侠的线程和姓名
- /// </summary>
- private static void InitData()
- {
- SuperMan = new Thread(new ParameterizedThreadStart(RunnerWork));
- SpiderMan = new Thread(new ParameterizedThreadStart(RunnerWork));
- SuperMan.Name = "SuperMan";
- SpiderMan.Name = "SpiderMan";
- }
- /// <summary>
- /// 裁判开始比赛,最后宣布胜者
- /// </summary>
- private static void JudgeWork()
- {
- Console.WriteLine("{0} PK {1}", SuperMan.Name, SpiderMan.Name);
- Console.WriteLine("比赛即将开始,请各位做好准备!");
- Console.WriteLine("预备!");
- Console.Read();
- //Superman起跑
- Console.WriteLine("回车枪响,Superman开始起跑!");
- Console.Beep(654, 1200);
- SuperMan.Start(500);
- //Monster起跑
- Console.WriteLine("回车枪响,SpiderMan开始起跑!");
- SpiderMan.Start(200);
- SuperMan.Join();
- SpiderMan.Join();
- //宣布赛跑结果
- Console.WriteLine("我宣布比赛结束");
- //程序暂停12秒
- Thread.Sleep(12000);
- }
- /// <summary>
- /// 赛跑的过程
- /// </summary>
- /// <param name="obj">赛跑参数</param>
- private static void RunnerWork(Object obj)
- {
- int length = Int32.Parse(obj.ToString());
- Thread CurrentThread = Thread.CurrentThread;
- string CurThreadName = CurrentThread.Name;
- int speed;
- //超人速度为20
- if (CurThreadName == SuperMan.Name)
- {
- speed = 50;
- }
- //蜘蛛侠速度为20
- else if (CurThreadName == SpiderMan.Name)
- {
- speed = 20;
- }
- //如果不可控线程进入,采用以下速度
- else
- {
- speed = 1;
- }
- Console.WriteLine("{0},开始起跑…………", CurThreadName);
- for (int count = speed; count <= length; count += speed)
- {
- Thread.Sleep(1000);
- Console.WriteLine("{0}……跑到了第{1}米", CurThreadName, count.ToString());
- }
- Console.WriteLine("{0},到达终点!了咧欢迎……", CurThreadName);
- }
- }
运行结果:
比赛刚刚开始,裁判即宣布结束,这不符合常理。仔细分析可以发现,程序可控制的进程一共有三个,即裁判、超人和蜘蛛侠,三个进程相互独立同时进行,所以裁判宣布比赛开始后即按照它的线程继续宣布结束。
我们可以这样:在裁判宣布比赛开始后,让蜘蛛侠和超人的线程执行完毕再执行裁判进程:
- //防止裁判的主进程先结束,让超人和蜘蛛侠的进程先执行完毕
- SuperMan.Join();
- SpiderMan.Join();
- Console.WriteLine("我宣布比赛结束");
这次的执行结果为:
赛跑结束,裁判才宣布比赛结束,但是还有问题,裁判总得宣布谁跑赢了吧,台底下这么多粉丝等着呢?这个我们可以用变量的方式保存署名,达到宣布谁为冠军的功能。
为了展示同步异步读写问题,我们让超人赛跑中去拯救世界,然后回来继续比赛;先到达终点的人,自己花时间找粉笔,然后在黑板上署名,其他人看到黑板上有名字就不能再写,裁判宣布署名的人为胜者。
- class MultiThread3
- {
- //署名用的黑板
- static string NameBoard = "";
- //定义两个线程,分别为超人和蜘蛛侠
- private static Thread SuperMan;
- private static Thread SpiderMan;
- //程序入口,比赛开始
- static void Main(string[] args)
- {
- //初始化数据
- InitData();
- //裁判吹哨,开始赛跑
- JudgeWork();
- }
- /// <summary>
- /// 初始化超人和蜘蛛侠的线程和姓名
- /// </summary>
- private static void InitData()
- {
- SuperMan = new Thread(new ParameterizedThreadStart(RunnerWork));
- SpiderMan = new Thread(new ParameterizedThreadStart(RunnerWork));
- SuperMan.Name = "SuperMan";
- SpiderMan.Name = "SpiderMan";
- }
- /// <summary>
- /// 裁判开始比赛,最后宣布胜者
- /// </summary>
- private static void JudgeWork()
- {
- Console.WriteLine("{0} PK {1}", SuperMan.Name, SpiderMan.Name);
- Console.WriteLine("比赛即将开始,请各位做好准备!");
- Console.WriteLine("预备!");
- Console.Read();
- //Superman起跑
- Console.WriteLine("回车枪响,SuperMan开始起跑!");
- Console.Beep(654, 1200);
- SuperMan.Start(500);
- //Monster起跑
- Console.WriteLine("回车枪响,SpiderMan开始起跑!");
- SpiderMan.Start(300);
- //防止裁判的主进程先结束,让超人和蜘蛛侠的进程先执行完毕
- SuperMan.Join();
- SpiderMan.Join();
- //宣布赛跑结果
- AnnounceWinner();
- //程序暂停12秒
- Thread.Sleep(12000);
- }
- /// <summary>
- /// 赛跑的过程
- /// </summary>
- /// <param name="obj">赛跑参数</param>
- private static void RunnerWork(Object obj)
- {
- int length = Int32.Parse(obj.ToString());
- Thread CurrentThread = Thread.CurrentThread;
- string CurThreadName = CurrentThread.Name;
- int speed;
- //超人速度为20
- if (CurThreadName == SuperMan.Name)
- {
- speed = 50;
- }
- //蜘蛛侠速度为20
- else if (CurThreadName == SpiderMan.Name)
- {
- speed = 20;
- }
- //如果不可控线程进入,采用以下速度
- else
- {
- speed = 1;
- }
- Console.WriteLine("{0},开始起跑…………", CurThreadName);
- for (int count = speed; count <= length; count += speed)
- {
- Thread.Sleep(1000);
- Console.WriteLine("{0}……跑到了第{1}米", CurThreadName, count.ToString());
- //超人跑到一半,去拯救世界
- if (count == length / 2)
- {
- if (CurThreadName == SuperMan.Name)
- {
- Console.WriteLine("世界末日来临,超人去拯救世界……");
- string waitInfo = "..";
- //超人拯救世界过程
- for (int j = 0; j <= 10; j++)
- {
- Console.WriteLine("超人拯救世界中" + waitInfo);
- waitInfo += "..";
- Thread.Sleep(1000);
- }
- Console.WriteLine("超人去拯救世界归来,继续赛跑……");
- }
- }
- }
- Console.WriteLine("{0},到达终点!乐咧欢迎……", CurThreadName);
- WriteName(CurThreadName);
- }
- /// <summary>
- /// 跑到重点线后,选手自己在黑板上署名
- /// </summary>
- /// <param name="name">选手姓名</param>
- private static void WriteName(string name)
- {
- //黑板上没名字,才可以署自己的名字
- if (NameBoard.Length == 0)
- {
- Console.WriteLine("{0}去找粉笔了……", name);
- //找粉笔花费的时间
- Thread.Sleep(9000);
- Console.WriteLine("{0}拿着粉笔回来了,开始署名……", name);
- NameBoard = name;
- Console.WriteLine("{0}署完名后,开心的离开了……", name);
- }
- //黑板上有署名时不能再署名
- else
- {
- Console.WriteLine("{0}发现已经署名,桑心的离开了……", name);
- }
- }
- /// <summary>
- /// 宣布比赛结果
- /// </summary>
- private static void AnnounceWinner()
- {
- Console.WriteLine("我是裁判,我宣布这次比赛的冠军是{0}", NameBoard);
- }
- }
运行结果:
可以看到明明是SuperMan还在拯救地球时,SpiderMan已经到达终点,而裁判宣布的冠军却是SuperMan。仔细分析一下程序即可知道:虽然SpiderMan先到达终点,并且先发现黑板是空的,但是在SpiderMan寻找粉笔的过程中,SuperMan到达终点,并且也发现黑板是空的,于是两人都写上了自己的名字,但是因为后者的会覆盖前者的,所以胜利者成了SuperMan,整个过程如下图所示:
问题出现的原因在于,SpiderMan到达以后看到黑板,SuperMan仍然看到黑板,即这个黑板对于两个人都是可写的,后者会覆盖前者的内容,这种方式为异步写。
怎么克服这个问题?可以使用Lock锁住临界区代码,如下:
- //定义一个对象类型的objLock
- private static object objLock = new object();
- /// <summary>
- /// 跑到重点线后,选手自己在黑板上署名
- /// </summary>
- /// <param name="name">选手姓名</param>
- private static void WriteName(string name)
- {
- //采用异步读方式,筛选掉已经看到署名的线程,提高效率
- //黑板上没名字,才可以署自己的名字
- if (NameBoard.Length == 0)
- {
- //因为上面为异步读,所以可能多个线程可以进入到这一步
- lock (objLock)
- {
- //同步读方式
- if (NameBoard.Length == 0)
- {
- Console.WriteLine("{0}去找粉笔了……", name);
- //找粉笔花费的时间
- Thread.Sleep(9000);
- Console.WriteLine("{0}拿着粉笔回来了,开始署名……", name);
- NameBoard = name;
- Console.WriteLine("{0}署完名后,开心地离开了……", name);
- }
- //黑板上有署名时不能再署名
- else
- {
- Console.WriteLine("{0}发现已经署名,桑心地离开了……", name);
- }
- }
- }
- }
需要注意的是,锁住的内容(非临界区代码)必须是共享型的引用型数据,因为如果是局部变量针对一个线程锁不锁对其它线程意义不大;采用引用数据类型,可以保证每个线程锁住内容都指向同一个地址。
为了直观显示,没有抽象出超人、蜘蛛侠和裁判的类,以上就是一个简单的多线程应用实例,当然这是多线程的冰山一角,更多的还有待在以后开发中实践,这次争取在考试系统使用多线程优化抽题和判分等功能。
(转) C#多线程赛跑实例的更多相关文章
- vc 基于对话框多线程编程实例——线程之间的通信
vc基于对话框多线程编程实例——线程之间的通信 实例:
- C#多线程编程实例 螺纹与窗口交互
C#多线程编程实例 螺纹与窗口交互 代码: public partial class Form1 : Form { //声明线程数组 Thread[] workThreads = new Thread ...
- c# 多线程 创建对象实例
本次的标题是我在写单例模式的博客时遇到的问题,所以今天专门写了的demo让自己记住怎么简单的使用多线程. 一直纠结的是怎么在for循环中多次实例化对象,好复现单例模式在没有加锁的情况下出现多个实例对象 ...
- linux下C语言多线程编程实例
用一个实例.来学习linux下C语言多线程编程实例. 代码目的:通过创建两个线程来实现对一个数的递加.代码: //包含的头文件 #include <pthread.h> #include ...
- Java单线程多实例和多线程多实例
最近写了一个程序,是采用多线程往redis里面写入数据,想统计一下一共写了多少条数据,于是用了一个static的全局变量count来累加,这块代码抽象出来就是这样的: public class Mul ...
- C#多线程编程实例 线程与窗体交互
C#多线程编程实例 线程与窗体交互 代码: public partial class Form1 : Form { //声明线程数组 Thread[] workThreads = ]; public ...
- java多线程编程实例
[转]这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下. 1.三个售票窗口同时出售20张票程序分析: ...
- Java 多线程 简单实例 (Runnable)
1.多线程实例 package second; public class A implements Runnable { public char stat = '*'; public void run ...
- Java 多线程 简单实例 (Thread)
package second; public class A extends Thread { public void run(){ for(int i = 1;i <= 10 ; i++){ ...
随机推荐
- 使用自定义字体 @font-face 小试
第一次了解到@font-face是小伙伴给我展示的功能 感觉奇妙的不得了 @font-face 是CSS3中的一个模块 使用它你就可以将你自定义的web字体 去实现一些奇妙的想法 首先先介绍 ...
- [js开源组件开发]js多选日期控件
js多选日期控件 详情请见:http://www.lovewebgames.com/jsmodule/calendar.html 它的github地址:https://github.com/tianx ...
- ASP.NET数据绑定技术
1.DataBinder.Eval()方法 DataBinder.Eval()方法是ASP.NET框架支持的一个静态方法,用来计算Late_Bound(后期绑定)数据绑定表达式,并随时将结果转换为字符 ...
- Java中的继承与组合(转载)
本文主要说明Java中继承与组合的概念,以及它们之间的联系与区别.首先文章会给出一小段代码示例,用于展示到底什么是继承.然后演示如何通过“组合”来改进这种继承的设计机制.最后总结这两者的应用场景,即到 ...
- android XMl 解析神奇xstream 五: 把复杂对象转换成 xml ,并写入SD卡中的xml文件
前言:对xstream不理解的请看: android XMl 解析神奇xstream 一: 解析android项目中 asset 文件夹 下的 aa.xml 文件 android XMl 解析神奇xs ...
- iOS--九宫格奥秘(UIView)(arc4random)
下面程序主要实现,九宫格的模型,每个小UIView的颜色随机产生的,用到了 arc4random函数: ; ; ; ; ; i<; i++) { ; j<; j++) { UIView * ...
- 多线程基础(七)GCD线程组+栅栏函数
1.GCD队列组 拦截通知和等待所有任务全部结束在继续往下执行|阻塞 需求:下载两张图片,等两张图片都下载完毕之后,合成图片(这个实例,复习的时候一定要凭空敲出代码练习,好记性不如烂键 ...
- IOS之--UI进阶--多控制器管理第一天
01-项目中常见的文件(LaunchScreen) Xcode5 框架是苹果事先已经导入进去的.在项目的结构当中就能够看到导入的框架. Xcode6 会自动导入一些觉见的框架.在项目结构当中,看不到已 ...
- eclipse创建本地maven
一.下载maven安装包和maven的eclipse插件 apache-maven-3.3.9-bin.zip eclipse-maven-plugin.zip 下载地址:http://pan.bai ...
- 【重构】 利用 cos 组件实现jsp中上传附件
利用JSP&Servlet重构项目 利用 cos 组件实现jsp中上传附件 fileUpload.jsp --> FileUploadController.java --> fil ...