本文是.NET異步和多線程系列的第五章,本章將綜合以前分享的異步和多線程來實現簡單的雙色球項目。html
話很少說,下面咱們直接進入本章主題。多線程
首先咱們來看下需求分析:dom
雙色球:投注號碼由6個紅色球號碼和1個藍色球號碼組成。異步
紅色球號碼:從01--33中選擇,不重複。ui
藍色球號碼:從01--16中選擇。this
球號碼隨機的規則:遠程獲取一個隨機數據,這個會有較長的時間損耗。spa
雙色球項目界面設計以下:線程
接下來咱們重點來看下代碼:設計
模擬獲取遠程隨機數:code
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) { //一、獲取隨機數 int index = new RandomHelper().GetRandomNumberDelay(0, 16); string sNumber = this._blueNums[index]; //二、更新界面 //this.lblBlue.Text = sNumber; //子線程不能操做控件,委託給主線程操做 //this.Invoke this.Invoke(new Action(() => { label.Text = sNumber; })); //三、循環 } } 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