线程阻塞UI
本文关键字:UI 线程 | 更新日期: 2023-09-27 18:23:49
我在Nutshell中使用C#中的一个示例。根据文本,下面的代码应该是非阻塞的,但我发现表单在5秒钟后才会显示。
private void Form1_Load(object sender, EventArgs e)
{
var tcs = new TaskCompletionSource<int>();
new Thread(() => {Thread.Sleep(5000); tcs.SetResult(42); }).Start();
Task<int> task = tcs.Task;
MessageBox.Show(task.Result.ToString());
}
我有一种感觉,这与Thread.Sleep()有关,它不是让新线程休眠,而是让主线程休眠。
为什么它会阻塞UI线程?
当您试图获得任务任务的结果时。结果主线程将被阻塞,直到任务完成执行为止(即结果可用)。如果不想等待异步操作完成,请使用task.ContinueWith
:
Task<int> task = tcs.Task;
task.ContinueWith(t => {
MessageBox.Show(t.Result.ToString());
});
顺便说一句,.NET 4.5中有一个很好的功能,可以在任务完成后恢复暂停的操作-异步方法:
private async void Form1_Load(object sender, EventArgs e)
{
var tcs = new TaskCompletionSource<int>();
new Thread(() => { Thread.Sleep(2000); tcs.SetResult(42); }).Start();
int result = await tcs.Task;
MessageBox.Show(result.ToString());
}
此方法将在开始等待任务结果后立即将控制权交给调用方。当结果可用时,方法将继续执行并显示消息。
事实上,正如@Servy在评论中指出的那样,返回void
的异步方法不是很好的做法(例如,用于错误处理),但有时将它们用于事件处理程序是可以的。
当您调用Task.Result.ToString()
(在MessageBox.Show
中)时,Task
类有一种机制,它会在实际给您结果之前等待任务完成(因为在Task
完成之前它实际上没有结果)
private void Form1_Load(object sender, EventArgs e)
{
var tcs = new TaskCompletionSource<int>();
new Thread(() => {Thread.Sleep(5000); tcs.SetResult(42); }).Start();
Task<int> task = tcs.Task;
Thread.Sleep(2500);
MessageBox.Show("Waited for 2.5secs on UI thread.");
MessageBox.Show(task.Result.ToString());
}
您会看到,它会在带有42的消息框之前显示2.5秒的消息框。(事实上,在此之前的2.5秒)。
你要找的是:
private void Form1_Load(object sender, EventArgs e)
{
var tcs = new TaskCompletionSource<int>();
new Thread(() => {Thread.Sleep(5000); tcs.SetResult(42); }).Start();
Task<int> task = tcs.Task;
task.ContinueWith(t => MessageBox.Show(t.Result.ToString()));
}
这将不会冻结UI线程。