线程阻塞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线程?

线程阻塞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线程。