.NET异步和多线程系列(五)- 多线程双色球项目
本文是.NET异步和多线程系列的第五章,本章将综合之前分享的异步和多线程来实现简单的双色球项目。
话不多说,下面我们直接进入本章主题。
首先我们来看下需求分析:
双色球:投注号码由6个红色球号码和1个蓝色球号码组成。
红色球号码:从01--33中选择,不重复。
蓝色球号码:从01--16中选择。
球号码随机的规则:远程获取一个随机数据,这个会有较长的时间损耗。
双色球项目界面设计如下:
接下来我们重点来看下代码:
模拟获取远程随机数:
using System; using System.Threading; namespace MyLottery.Common { /// <summary> /// 解决随机数重复问题 /// 同时模拟远程请求的随机延时 /// </summary> public class RandomHelper { /// <summary> /// 随机获取数字并等待一段时间 /// </summary> public int GetRandomNumberDelay(int min, int max) { Thread.Sleep(this.GetRandomNumber(500, 1000)); //随机休息一下 return this.GetRandomNumber(min, max); } /// <summary> /// 获取随机数(解决重复问题) /// </summary> public int GetRandomNumber(int min, int max) { Guid guid = Guid.NewGuid(); //每次都是全新的ID string sGuid = guid.ToString(); int seed = DateTime.Now.Millisecond; for (int i = 0; i < sGuid.Length; i++) { switch (sGuid[i]) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': seed = seed + 1; break; case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': seed = seed + 2; break; case 'o': case 'p': case 'q': case 'r': case 's': case 't': seed = seed + 3; break; case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': seed = seed + 3; break; default: seed = seed + 4; break; } } Random random = new Random(seed); return random.Next(min, max); } } }
界面核心代码如下:
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using MyLottery.Common; namespace MyLottery { /// <summary> /// 多线程双色球项目 /// 需求: /// 双色球:投注号码由6个红色球号码和1个蓝色球号码组成。 /// 红色球号码:从01--33中选择,不重复。 /// 蓝色球号码:从01--16中选择。 /// /// 球号码随机的规则:远程获取一个随机数据,这个会有较长的时间损耗。 /// </summary> public partial class Form1 : Form { public Form1() { InitializeComponent(); this.btnStart.Enabled = true; this.btnStop.Enabled = false; } #region 字段和属性 #region 数据 /// <summary> /// 红球集合 其实可以写入配置文件 /// </summary> public string[] _redNums = { "01","02","03","04","05","06","07","08","09","10", "11","12","13","14","15","16","17","18","19","20", "21","22","23","24","25","26","27","28","29","30", "31","32","33" }; /// <summary> /// 蓝球集合 /// </summary> private string[] _blueNums = { "01","02","03","04","05","06","07","08","09","10", "11","12","13","14","15","16" }; #endregion 数据 private bool _isGoOn = true; private List<Task> _taskList = new List<Task>(); private static readonly object _lockObj = new object(); #endregion 字段和属性 /// <summary> /// 点击开始 /// </summary> private void btnStart_Click(object sender, EventArgs e) { try { #region 初始化动作 this.btnStart.Text = "运行ing"; this.btnStart.Enabled = false; this._isGoOn = true; this._taskList = new List<Task>(); this.lblBlue.Text = "00"; this.lblRed1.Text = "00"; this.lblRed2.Text = "00"; this.lblRed3.Text = "00"; this.lblRed4.Text = "00"; this.lblRed5.Text = "00"; this.lblRed6.Text = "00"; #endregion 初始化动作 Thread.Sleep(1000); foreach (var control in this.gboSSQ.Controls) { if (control is Label) { Label label = (Label)control; if (label.Name.Contains("Blue")) { _taskList.Add(Task.Run(() => { try { while (_isGoOn) { //1、获取随机数 int index = new RandomHelper().GetRandomNumberDelay(0, 16); string sNumber = this._blueNums[index]; //2、更新界面 //this.lblBlue.Text = sNumber; //子线程不能操作控件,委托给主线程操作 //this.Invoke this.Invoke(new Action(() => { label.Text = sNumber; })); //3、循环 } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } })); } else if (label.Name.Contains("Red")) { _taskList.Add(Task.Run(() => { try { while (_isGoOn) { int index = new RandomHelper().GetRandomNumberDelay(0, 33); string sNumber = this._redNums[index]; //可能重复 得去重 //检测下是否重复,直接比对界面 //获取随机 比对界面 更新 lock (_lockObj) { List<string> usedNumberList = this.GetUsedRedNumbers(); if (!usedNumberList.Contains(sNumber)) { this.Invoke(new Action(() => { //Thread.Sleep(10); label.Text = sNumber; //委托给主线程操作 })); //实际上是同步的 } } } } catch (Exception ex) { Console.WriteLine(ex.Message); } })); } } } Task.Factory.ContinueWhenAll(this._taskList.ToArray(), tArray => { this.ShowResult(); this.Invoke(new Action(() => { this.btnStart.Enabled = true; this.btnStop.Enabled = false; })); }); Task.Delay(10 * 1000).ContinueWith(t => { this.Invoke(new Action(() => { this.btnStop.Enabled = true; })); }); } catch (Exception ex) { Console.WriteLine("双色球启动出现异常:{0}", ex.ToString()); } } /// <summary> /// 点击结束 /// </summary> private void btnStop_Click(object sender, EventArgs e) { this._isGoOn = false; #region 死锁 //此处主线程不能直接WaitAll 界面会卡死 死锁了 //主线程要等着子线程完成后才能往下走,而子线程又要等着主线程帮忙干件事后才能往下走,两者都在等着对方,这就导致了死锁。 //{ // Task.WaitAll(this._taskList.ToArray()); // this.ShowResult(); //} #endregion 死锁 #region ShowResult这事情在这里做不合适,应该放到回调里面去做比较合适 //此处要等子线程完成的话应该再开一个子线程去等才对 //{ // Task.Run(() => // { // Task.WaitAll(this._taskList.ToArray()); // this.ShowResult(); // }); //} #endregion ShowResult这事情在这里做不合适,应该放到回调里面去做比较合适 } #region 公共方法 /// <summary> /// 获取已经使用过的红色数字 /// </summary> private List<string> GetUsedRedNumbers() { List<string> usedNumberList = new List<string>(); foreach (var controler in this.gboSSQ.Controls) { if (controler is Label) { if (((Label)controler).Name.Contains("Red")) { usedNumberList.Add(((Label)controler).Text); } } } return usedNumberList; } /// <summary> /// 弹框提示数据 /// </summary> private void ShowResult() { MessageBox.Show(string.Format("本期双色球结果为:{0} {1} {2} {3} {4} {5} 蓝球{6}" , this.lblRed1.Text , this.lblRed2.Text , this.lblRed3.Text , this.lblRed4.Text , this.lblRed5.Text , this.lblRed6.Text , this.lblBlue.Text)); } #endregion 公共方法 } }
运行结果如下:
Demo源码:
链接:https://pan.baidu.com/s/1J2OFDxI4dHspkrh9CJy2Sg 提取码:h2l3
此文由博主精心撰写转载请保留此原文链接:https://www.cnblogs.com/xyh9039/p/13616391.html
版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!