线程阻止UI

时间:2013-07-17 17:54:16

标签: c# task-parallel-library

我在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线程?

2 个答案:

答案 0 :(得分:11)

当您尝试获取任务task.Result的结果时,主线程将被阻止,直到任务完成它的执行(即结果可用)。如果您不想等待异步操作完成,请使用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的异步方法不是很好的做法(例如,用于错误处理),但有时可以将它们用于事件处理程序。

答案 1 :(得分:5)

当您调用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());
}

您会看到它在消息框之前显示的是2.5秒消息框,其中包含42.(事实上,在此之前的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线程。