C# 为什么Thread.Sleep()会冻结表单?
我尝试使用C# 为什么Thread.Sleep()会冻结表单?,c#,multithreading,user-interface,sleep,freeze,C#,Multithreading,User Interface,Sleep,Freeze,我尝试使用Thread.Sleep()进行实验。我用一个按钮创建了基本的Windows窗体应用程序 private void button1_Click(object sender, EventArgs e) { Thread thread1 = new Thread(DoStuff); thread1.Start(); for (int i = 0; i < 100000; i++) {
Thread.Sleep()
进行实验。我用一个按钮创建了基本的Windows窗体应用程序
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
button1.Text +=".";
}
}
public void DoStuff()
{
//DoStuff
}
private void按钮1\u单击(对象发送者,事件参数e)
{
螺纹1=新螺纹(DoStuff);
thread1.Start();
对于(int i=0;i<100000;i++)
{
睡眠(500);
按钮1.文本+=”;
}
}
公共空间
{
//多斯塔夫
}
当我点击我的按钮时,
DoStuff
方法工作正常,但是GUI冻结,什么也没有发生。有人能解释一下原因吗?线程。睡眠只会睡眠当前线程(即阻止它做任何事情,如重画、处理单击等),在您的情况下,这就是UI线程。如果将Sleep
放入DoStuff
中,您将不会体验到阻塞,因为您将处于单独的线程中,尽管您无法更新按钮1。根据.Net的版本,您使用的是使用任务并行库的考虑,例如:
private TaskScheduler _uiScheduler;
public Form1()
{
InitializeComponent();
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
// Create a task on a new thread.
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
// Create a new task on the UI thread to update the button
Task.Factory.StartNew(() =>
{ button1.Text += "."; }, CancellationToken.None, TaskCreationOptions.None, _uiScheduler);
}
});
}
private TaskScheduler\u uiScheduler;
公共表格1()
{
初始化组件();
_uiScheduler=TaskScheduler.FromCurrentSynchronizationContext();
}
私有无效按钮1\u单击(对象发送者,事件参数e)
{
螺纹1=新螺纹(DoStuff);
thread1.Start();
//在新线程上创建任务。
Task.Factory.StartNew(()=>
{
对于(int i=0;i<100000;i++)
{
睡眠(500);
//在UI线程上创建新任务以更新按钮
Task.Factory.StartNew(()=>
{button1.Text+=“;”},CancellationToken.None,TaskCreationOptions.None,_uiScheduler);
}
});
}
重新排列代码,如下所示
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
}
public void DoStuff()
{
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
//Invoke goes here
}
}
private void按钮1\u单击(对象发送者,事件参数e)
{
螺纹1=新螺纹(DoStuff);
thread1.Start();
}
公共空间
{
对于(int i=0;i<100000;i++)
{
睡眠(500);
//调用在这里
}
}
现在,您可以在一个单独的线程中运行您的工作,并为日常工作(与绘图相关或其他工作)释放UI线程
注意-现在您需要调用方法来更改按钮文本,否则您将收到“跨线程操作无效”的警告
有关调用的更多信息-要保持UI活动,需要主UI线程为其消息泵提供服务。它只能在不处理UI事件时执行此操作。在您的例子中,函数
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
button1.Text +=".";
}
}
private void按钮1\u单击(对象发送者,事件参数e)
{
螺纹1=新螺纹(DoStuff);
thread1.Start();
对于(int i=0;i<100000;i++)
{
睡眠(500);
按钮1.文本+=”;
}
}
大约在100000*500
毫秒内不返回。此事件处理程序执行时,UI线程正忙。它正在执行此事件处理程序。因此,它无法维修信息泵。因此,应用程序的UI会冻结。为此,您最好使用计时器,但如果您希望当前代码正常工作,则需要添加application.DoEvents()
更新按钮后,如果您是多线程新手,我强烈建议您查看任务并行库(TPL)。它简化了线程,并为您提供了一些工具来帮助确保回调(延续)线程在UI线程上发生
TPL位于System.Threading.Tasks命名空间中
更新:刚刚看到你对.NETV2的评论。TPL是在.NET v3.5中引入的,可能最晚是在v4中引入的。编辑:
经过几年的编程,我现在知道这是一种多么可怕的做法。不要做我下面建议的任何事情。都是废话。一个更合适的解决方案是将所有密集型方法都异步地放在一起。无论如何,不要做我下面提到的事情
以上所有方法都可以,但我建议只使用异步void。
Sleep()。不要引用我的话,我相信async会专门为该函数创建一个新线程
下面我介绍了一个更好的睡眠功能
要调用函数sleep(毫秒)
,
将“毫秒”替换为您希望睡眠的毫秒数
功能代码:
public async void asleep(int time){
await Task.Delay(time)
}
我知道UI会休眠,但我不明白的是为什么它会休眠超过500毫秒?@GugaMelkadze只有当按钮1\u Click()
方法返回时,UI才能更新,而且很明显,它在休眠很长时间后才会返回。谢谢,但我使用的是.NET 2.0。你的Thread.sleep()调用是for{}的一部分语句,因此它被执行100000次。您当前的UI线程已休眠近14个小时@GugaMelkadze如果你使用的是非常过时的.Net版本,你应该在你的问题中添加一个标签,否则人们会认为你没有使用过时的版本,并且会浪费他们的时间用一个不可用的解决方案回答您的问题。您需要调用文本
访问,因为它位于与创建控件的线程不同的线程上。我在编辑中添加了该部分:)只是出于兴趣,您使用的是.Net的哪个版本?@MatthewWatson我使用的是.Net 2.0永远不要使用Application.DoEvents()
!这是邪恶的。是的,我不会。我会使用每500毫秒一次的计时器