先阐明需求,红色球六个号码,从01-33,蓝色球01-16,依次随机变化,
有图有真相
根据这个需求,博主第一次尝试用Timer定时器,很快解决这个问题,不过这里每个球是依次刷新数据,
当然重点这里为了复习巩固前几日的学习的多线程,立即反应过来每个双色球,都可以用一个子线程开启,执行一个循环进行赋值,为了考虑不断更改效果,每个子线程休眠随机时间,0.5-1s,具体代码如下:
重难点是避免,六个子线程产生红色球,号码重复,这里采用加锁方式,锁住判断已有红色球号码,
有即重新获取随机值,
知识点:1若是子线程无法对主线程中的控件进行赋值,考虑采用委托,
this.Invoke(new Action(()=>{}));
知识点:2考虑六个子线程,同时执行代码,同时判断红色球号码,已经产生,采用加锁方式
lock (objlock)
{
List<string> list = GetAllLabelRandomNum();
if (!list.Contains(tmp))
{
}
}
知识点:3开奖时,获取双色球结果,本质是等待所有子线程结束,然后回调弹出结果
不能在子线程中,直接waitall等待所有子线程结束,形成死锁
(子线程委托主线程办点事,主线程等待子线程结束,结果主线程,和子线程中互相等待,形成死锁)
知识点:4在写代码,由于粗心大意,这里label从左往右依次是label1,label7,label6,label5,label4,label3, label2
写错代码 导致子线程中数组超过索引,一直报错,但是这里用主线程waitAll,加上AggragateException才捕捉到异常
catch (AggregateException ex)
{
foreach (Exception item in ex.InnerExceptions)
{
Console.WriteLine(item.Message);
}
throw;
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Form1
{
public partial class NoTimerMutityThread : Form
{
private string[] redNums=new string[33];
private string[] blueNums=new string[16];
private static readonly object objlock = new object();
private bool flag = true;
private List<Task> listtask = new List<Task>();
public NoTimerMutityThread()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
flag = true;
AddNums(redNums,blueNums);
button2.Enabled = true;
button1.Enabled = false;
CreateRandom();
//Console.WriteLine("测试");
}
//随机生成数字,并且绑定到label标签上进行显示
//这种方式有大量的重复数字,主要是随机数重复比较多
private void CreateRandom()
{
try
{
//数字生成频率也不一致,即不采用定时器,而是while,一直赋值,但是在获取值的过程中有不同的休眠时间
//执行时间
//Random r = new Random();
List<string> liststring = new List<string>();
string tmp = string.Empty;
//单独给每个label赋值有些low,尝试获取控件集合,依次赋值
foreach (Control item in this.Controls)
{
if (item is Label)
{
//蓝色球
if (((Label)item).Name == "label2")
{
//在多线程里面开通一个循环随机生成数,并进行赋值
listtask.Add(Task.Run(() =>
{
while (flag)
{
//这里为何运行一阵,有的label值不再变化,像停止,
tmp = blueNums[GetRandomNumberDelay(0, 16)];
((Label)item).Text = tmp;
//Console.WriteLine(tmp);
//liststring.Add(tmp);
//GetAllLabelRandomNum();
}
}));
}
else
{
listtask.Add(Task.Run(() =>
{
while (flag)
{
tmp = redNums[GetRandomNumberDelay(0, 33)];
//测试需要一定的运气
/*
*************start**********
22
09
08
18
15
22
*************end**********
*************start**********
30
12
19
30
18
11
*************end***********/
lock (objlock)
{
List<string> list = GetAllLabelRandomNum();
if (!list.Contains(tmp))
{
//否则再次循环判断,找到不包括才赋值
//有一种情况是,子线程中不能给主线程中的控件赋值,
//采用做法是:利用委托,委托到主线程赋值
this.Invoke(new Action(() =>
{
((Label)item).Text = tmp;
}));
//((Label)item).Text = tmp;
}
}
//有个问题,每个子线程判断,有可能在某一瞬间,同时不包括某一数字,结果都赋值同一数字
//获取所有的时候,必须锁住,轮流过来判断
//liststring.Add(tmp);
//每个子线程确实都进行获取全部已有数字,的的确确出现重复数字
//那么非常明显,加上判断是否已经拥有这个值,有即重新获取随机数
}
}));
}
//这里输出空,
//Console.WriteLine(tmp);
}
}
//多线程需要用到它,将错误集合捕捉,否则主线程无法捕捉到子线程中错误
//Task.WaitAll(listtask.ToArray());
//这里一直在向集合里面添加数值,不断增大增大,
//应该获取控件的已存在,已经赋值的情况
//if (liststring.Distinct().Count()<=5)
//{
//for (int i = 0; i < liststring.Count; i++)
//{
// Console.WriteLine(liststring[i]);
//}
//}
//有一个瞬间,每个线程随机出来的值,可能重复,
//为了去除重复,先获取控件中所有的值,
//一旦获取到重复数字,马上停止输出
}
catch (AggregateException ex)
{
foreach (Exception item in ex.InnerExceptions)
{
Console.WriteLine(item.Message);
}
throw;
}
}
private List<string> GetAllLabelRandomNum(){
List<string> result = new List<string>();
foreach (Control item in this.Controls)
{
if (item is Label && ((Label)item).Name != "label2")
{
result.Add(item.Text);
}
}
//如果去重小于6,有重复就输出,但是不算00
if (result.Distinct().Count() < 6 &&!result.Contains("00"))
{
Console.WriteLine("*************start**********");
for (int i = 0; i < result.Count; i++)
{
Console.WriteLine(result[i]);
}
Console.WriteLine("*************end**********");
}
return result;
}
private void AddNums(string[] redNums, string[] blueNums)
{
for (int i = 1; i < 34; i++)
{
if (i < 10)
{
redNums[i - 1] = "0" + i;
}
else {
redNums[i - 1] = "" + i;
}
}
for (int i = 1; i < 17; i++)
{
if (i < 10)
{
blueNums[i - 1] = "0" + i;
}
else
{
blueNums[i - 1] = "" + i;
}
}
}
private void button2_Click(object sender, EventArgs e)
{
//一旦停止,那么
//将死循环停止,true改为false即可,但是程序并不停止
//获取最终的双色球所有结果,先等他执行完再进行获取结果
flag = false;
//等待所有线程结束,但是不能用waitall,因为会卡顿
//一旦子线程委托主线程,这里主线程又等待子线程结束,容易出现死锁,
//一般都是进行再次开线程,
//Task.Run(() => {
// Task.WaitAll(listtask.ToArray());
// MessageBox.Show(string.Format("本期双色球结果:{0},{1},{2},{3},{4},{5} {6}", label1.Text, label7.Text, label6.Text, label5.Text, label4.Text, label3.Text, label2.Text));
//});
//其实这里结束,是等待所有线程结束,进行一个回调
//第二个参数是带t参数,t为Task[]的Action
Task.Factory.ContinueWhenAll(listtask.ToArray(), (t) => {
MessageBox.Show(string.Format("本期双色球结果:{0},{1},{2},{3},{4},{5} {6}", label1.Text, label7.Text, label6.Text, label5.Text, label4.Text, label3.Text, label2.Text));
});
button1.Enabled = true;
button2.Enabled = false;
}
private int GetRandomNumberDelay(int min, int max)
{
//
Thread.Sleep(GetRandomNumber(500, 1000));
return GetRandomNumber(min, max);
}
private int GetRandomNumber(int min, int max)
{
//原理就是根据随机种子生成更加随机的随机数
string guid = Guid.NewGuid().ToString();
int seed = DateTime.Now.Millisecond;
//字符串默认,就是字符数组
for (int i = 0; i < guid.Length; i++)
{
switch (guid[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':
case 'u':
seed = seed + 3;
break;
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
seed = seed + 4;
break;
default:
break;
}
}
//由于guid字符串不同,最终的seed被多次循环添加后,数字肯定也不同
Random random = new Random(seed);
return random.Next(min, max);
}
}
}
总结,通过亲自动手实践,收获不少,手写是面向对象编程,标准规范是类中全局属性,方法,若是不对外采用private访问级别
对面向对象编程,理解更深一层