给定:2 种形式。
主窗体包含一个调用辅助窗体(窗体 2)的按钮。
Form 2 包含一个暂停System.Threading.Thread.Sleep(10000)来模拟复杂的工作(加载组件、多个图像、渲染)。
任务:按下主窗体上的按钮时立即显示窗体 2。在这种情况下,在按下按钮之前,用户不应该看到表单 2(甚至闪烁)。用户将在程序启动后不早于 10 秒按下该按钮。
我尝试了在不同线程中运行的选项,我尝试只使用Task, BackgroundWorker, ThreadPool。它不起作用,通常,问题出在第二个按钮调用表单时。
什么想法?
我如何尝试解决问题的一个例子:
private void Form1_Load(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s, ea) =>
{
frm.Show();
// В форме 2 стоит this.Visible = false;
};
bw.RunWorkerAsync();
}
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("button1_Click");
this.Invoke(new MethodInvoker(frm.ShowForm));
}
// Form 2:
public void ShowForm()
{
Console.WriteLine("Form2 showForm");
this.Visible = true;
this.WindowState = FormWindowState.Normal;
this.BringToFront();
}
不要用长操作阻塞 UI 线程。异步执行它们。
所以不要做类似的事情
Thread.Sleep()。在任何情况下都不应调用 UI 线程来调用方法(比方说SomeMethod(…))返回Task<T>,这只会SomeMethod(…).Result导致死锁。假设有一个方法返回 JSON:
这是错误的做法,这就是我们陷入僵局的原因。
相反,这样做是正确的:
结论
我们通过这种方式解决释放UI线程的任务:我们
async给事件处理程序添加一个修饰符(this isLoad等Click,他们大多是getasync void)。在它的更深处,我们在.Task的帮助下执行任何操作await。所以最终的结果是这样的:
所有长逻辑都应该放到第二种形式的构造函数中(我觉得这是合乎逻辑的)。并进一步在第一个开始时,在单独的流程中创建第二个表单。记住这一点。通过点击按钮,很容易显示出来。是这样的:
这段代码的唯一问题是,如果用户在加载表单之前单击按钮,一切都会崩溃。但在这里您可以只检查 Null,或者使用任务并在按下按钮时检查任务是否已完成)。