多线程应用——双色球
通过多线程实现双色球选号,
要求:1、红球从01到33不能有重复;2、蓝球从1到16
效果如下:
难点:
1、多线程实现
2、跨线程修改UI
3、线程安全(红球不能有重复)
4、取消线程
5、线程取消导致的死锁
代码如下:
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace UnionLotto { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Load += Form1_Load; this.FormClosed += Form1_FormClosed; } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { System.Environment.Exit(0);//推出所有进程,防止程序关闭后报错 } private void Form1_Load(object sender, EventArgs e) { //红球集合 for (int i = 0; i < redNums.Length; i++) { redNums[i] = (i + 1).ToString("00"); } //蓝球集合 for (int i = 0; i < blueNums.Length; i++) { blueNums[i] = (i + 1).ToString("00"); } this.btnStop.Enabled = false; } string[] redNums = new string[33];//红色球集合 string[] blueNums = new string[16];//蓝色球集合 private static object Lock_object = new object();//线程锁 CancellationTokenSource cts; List<Task> tasks = new List<Task>(); private void btnStart_Click(object sender, EventArgs e) { this.btnStart.Text = "运行..."; this.btnStart.Enabled = false; this.btnStop.Enabled = true; cts = new CancellationTokenSource(); //先全部赋值为“00” foreach (var item in groupBox1.Controls) { if (item is Label) { Label label = (Label)item; label.Text = "00"; } } foreach (var item in groupBox1.Controls) { if (item is Label) { Label label = item as Label; if (label.Name.Contains("Blue"))//蓝球 { //1、通过线程获得随机蓝球 tasks.Add(Task.Run(() => { while (!cts.IsCancellationRequested) { //获得随机数 int index = RandomHelper.GetRandomNumberDelay(0, 15); //2、跨线程修改UI this.Invoke(new Action(() => { label.Text = blueNums[index]; })); } })); } else//红球 { //1、通过线程获得随机红球 tasks.Add(Task.Run(() => { while (!cts.IsCancellationRequested) { //获得随机数 int index = RandomHelper.GetRandomNumberDelay(0, 32); string redNum = redNums[index]; lock (Lock_object)//3、线程锁,保证每个线程取值安全 { List<string> redList = GetCurrentRedNumList(); //如果现有label的text中没有与redNum相同的则显示,否则进入下一循环 if (!redList.Contains(redNum)) { //2、跨线程修改UI this.Invoke(new Action(() => { label.Text = redNum; })); } } } })); } } } } //获取红球集合 private List<string> GetCurrentRedNumList() { List<string> redNumList = new List<string>(); foreach (var item in groupBox1.Controls) { if (item is Label) { Label label = item as Label; if (label.Name.Contains("Red")) { redNumList.Add(label.Text); } } } return redNumList; } private void btnStop_Click(object sender, EventArgs e) { cts.Cancel();//4、取消线程 //5、创建子线程来等待所有线程结束,否则将导致死锁 Task.Run(() => { Task.WaitAll(tasks.ToArray());//4、线程取消后,等带所有线程完成 MessageBox.Show(RedNum1.Text + "," + RedNum2.Text + "," + RedNum3.Text + "," + RedNum4.Text + "," + RedNum5.Text + "," + RedNum6.Text + "," + BlueNum.Text + ","); this.Invoke(new Action(() => { this.btnStart.Text = "start"; this.btnStart.Enabled = true; this.btnStop.Enabled = false; })); }); } } }
代码中获得随机数的方法如下:
using System; using System.Threading; namespace UnionLotto { public class RandomHelper { /// <summary> /// new Random().Next(1,100);多线程同时执行结果很高概率相同, /// 是用的当前事件为seed,时间相同结果相同 /// /// 解决随机数重复问题 /// 同事模拟远程请求的随机延时 /// </summary> /// <param name="min"></param> /// <param name="max"></param> /// <returns></returns> public static int GetRandomNumberDelay(int min, int max) { Thread.Sleep(GetRandomNumber(300, 500)); return GetRandomNumber(min, max); } public static int GetRandomNumber(int min, int max) { Guid guid = Guid.NewGuid(); 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 + 4; break; default: break; } } Random random = new Random(seed); return random.Next(min, max); } } }