C# C中的线程问题#
我试图生成一个随机的水果,并在GUI上以标签的形式显示它。我正在使用这个代码来完成它C# C中的线程问题#,c#,multithreading,C#,Multithreading,我试图生成一个随机的水果,并在GUI上以标签的形式显示它。我正在使用这个代码来完成它 partial class Form1 : Form { int MagicNumber = 0; List<string> NameList = new List<string>(); Random r = new Random(); public Form1() { InitializeComponent(); }
partial class Form1 : Form
{
int MagicNumber = 0;
List<string> NameList = new List<string>();
Random r = new Random();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
NameList.Add("Apples");
NameList.Add("Pears");
NameList.Add("Oranges");
NameList.Add("Bananas");
NameList.Add("Kiwi");
for (int i = 0; i < 8; i++)
{
Thread t = new Thread(new ThreadStart(Display));
t.Start();
label1.Text = NameList[MagicNumber];
Thread.Sleep(1000);
}
}
private void Display()
{
MagicNumber = r.Next(5);
}
}
部分类表单1:表单
{
int MagicNumber=0;
列表名称列表=新列表();
随机r=新随机();
公共表格1()
{
初始化组件();
}
私有无效按钮1\u单击(对象发送者,事件参数e)
{
姓名列表。添加(“苹果”);
姓名列表。添加(“梨”);
姓名列表。添加(“橙子”);
姓名列表。添加(“香蕉”);
姓名列表。添加(“猕猴桃”);
对于(int i=0;i<8;i++)
{
线程t=新线程(新线程开始(显示));
t、 Start();
label1.Text=名称列表[MagicNumber];
睡眠(1000);
}
}
专用void Display()
{
MagicNumber=r.Next(5);
}
}
问题在于,在GUI中,我只看到选择结果的最后一个结果,而没有看到它们是如何从一个迭代跳到另一个迭代的。我想这段代码会让我有可能看到水果是如何变化的,直到最后一个被选中,当我8岁的时候
请如果你有一个想法,为什么这个代码没有显示如何选择的水果标签给我一只手
谢谢。有一种更好的方法来选择随机项目:
label1.Text = NameList.OrderBy(f => Guid.NewGuid()).First();
在不同线程上随机化本身就是一个坏主意。您观察到的表单不刷新问题是由于您的函数阻塞了GUI线程,并阻止了窗口在运行时重新绘制。它连续运行了8秒。GUI线程需要处理消息以允许重新绘制窗口 但除了你观察到的,它至少有两个与线程相关的理论问题:
r.Next
不是线程安全的。因此,同时从两个不同的线程调用它可能会损坏Random
实例。在实践中也不会发生,因为延迟太长,一个线程很可能在下一个线程启动之前完成问题在于,当您执行事件处理程序或从中调用的函数时,更改会在最后呈现。尝试在线程内更改标签文本,从中获得随机数。您还必须在表单构造函数中将CheckForIllegalCrossThreadCalls属性设置为false。您似乎混淆了计时器和线程。在这种情况下,我想你想要的是一个计时器;明确地您可以这样做:
partial class Form1 : Form
{
Timer timer = new Timer();
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
timer.Tick += (s, e) =>
{
if (i < 8)
{
label1.Text = nameList[r.Next(5)];
i++;
}
else
timer.Stop();
};
timer.Interval = 1000;
timer.Start();
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(Display));
t.Start();
}
private void Display()
{
for(int i = 0; i < 8; i++)
{
label1.Text = NameList[r.Next(5)];
Thread.Sleep(1000);
}
}
Thread.Sleep()
总是使调用它的线程处于睡眠状态——因此,也许这就是您想要做的
但是,这可能会引发线程同步异常——窗体阻止您访问来自另一个线程的UI控件,因为它可能处于无效状态(即在渲染的中间或做其他易失性的操作)。System.Windows.Forms.Timer实际上是在UI线程上运行的,因此更易于管理。
您的方法有缺陷,但您可能需要了解代码中的情况,因为它可能会帮助您找到更好的方法: for (int i = 0; i < 8; i++)
{
Thread t = new Thread(new ThreadStart(Display));
t.Start();
label1.Text = NameList[MagicNumber];
Thread.Sleep(1000);
}
for(int i=0;i<8;i++)
{
线程t=新线程(新线程开始(显示));
t、 Start();
label1.Text=名称列表[MagicNumber];
睡眠(1000);
}
您正在浏览,每次单击按钮时创建八个线程。你有理由创建八个线程吗?如果是这样,您可能希望在init
函数中创建它们一次并重用它们
然后这里有一个竞赛,你的线程在使用之前可能没有时间更改MagicNumber
,因为循环启动线程,然后在进入睡眠之前立即更改文本
睡眠是另一个问题,因为您还没有脱离主(事件)线程,所以文本在退出该事件处理程序之前不会更改
如果你想看到文本的变化,那么你需要离开主线程,在第二个线程中完成八次循环
然后,您可以将该线程置于睡眠状态,因为主线程可以自由进行更改,您将看到它
以下是MS的一篇文章,有点过时,但其基本思想应该对您有所帮助:
现在可以为线程使用lambda表达式,如下所示:
只需调用
Application.DoEvents()代码>将文本指定给标签后-这将刷新UI
顺便说一句,我不明白你为什么要使用线程生成随机数你只在一个单独的线程中生成随机数(访问时带有竞态条件),你用线程锁定UI。睡眠只会看到最后的结果,你需要在Display()中移动代码然后你必须调用Control.Invoke从新线程访问UI线程看起来你在尝试C风格的线程,这在C中是不同的,启动一个新线程只是为了设置一个int的值是非常非常非常无效的。你知道NewGuid的性能成本吗??为什么不只是随机的?这项技术来自杰夫·阿特伍德。这项技术可能来自Donald Knuth,它仍然是愚蠢的,而且毫无理由的昂贵。在Jeff的原始示例中,它比这里更有意义。因为写一个正确的洗牌并不是那么容易。但是我仍然会使用加密PRNG,而不是创建guid来获取不可预测的数字。而且我在NewGuid
的文档中没有看到有关如何生成guid的指示。虽然第4类GUI是随机的,但还有其他不会产生随机数的GUI生成方法。您只能从GUI线程更改标签文本。因为你可以禁用异常警告