简体   繁体   中英

Thread Blocking the UI

I was following an example from C# in a Nutshell. According to the text the following code is supposed to be non blocking, but I find that the form will not display until the 5 seconds have passed.

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());
}

I have a feeling it's something to do with Thread.Sleep() and instead of putting the new Thread to sleep, it's putting the main Thread to sleep.

Why is it blocking the UI thread?

When you are trying to get result of task task.Result main thread will be blocked until task will finish it's execution (ie result will be available). Use task.ContinueWith if you don't want to wait for async operation completion:

Task<int> task = tcs.Task;
task.ContinueWith(t => {         
     MessageBox.Show(t.Result.ToString());
});

BTW there is nice feature in .NET 4.5 for resuming suspended operation when task is completed - async methods:

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());
}

This method will yield control to caller immediately after starting to wait for task result. When result will be available, method will continue execution and show message.

Actually as @Servy pointed in comments, async method which return void is not very good practice (eg for error handling), but sometimes it's OK to use them for event handlers.

When you call Task.Result.ToString() (in the MessageBox.Show ) the Task class has a mechanism that waits for the task to be finished before actually giving you the result (as it doesn't actually have it until the Task finishes. Here's my proof:

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());
}

You will see that it shows you the 2.5sec message box before the messagebox with 42. (in fact, 2.5 seconds before that).

What you are looking for is this:

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()));
}

This will not freeze the UI thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM