Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 798
James Jeffery Avatar asked Jul 17 '13 17:07

James Jeffery


People also ask

What is an UI thread?

User Interface Thread or UI-Thread in Android is a Thread element responsible for updating the layout elements of the application implicitly or explicitly. This means, to update an element or change its attributes in the application layout ie the front-end of the application, one can make use of the UI-Thread.

What happens when a thread is blocked?

The running thread will block when it must wait for some event to occur (response to an IPC request, wait on a mutex, etc.). The blocked thread is removed from the running array, and the highest-priority ready thread that's at the head of its priority's queue is then allowed to run.

Is the main thread the UI thread?

It is also almost always the thread in which your application interacts with components from the Android UI toolkit (components from the android. widget and android. view packages). As such, the main thread is also sometimes called the UI thread.

What is blocking the main thread?

Put simply: The Main Thread is where the browser does most of the work needed to display a page. If we keep the Main Thread blocked, it can't perform its crucial tasks. This leads to slow load times, unresponsive pages, and a bad user experience.


2 Answers

When you are trying to get result of task task.Result main thread will be blocked until task will finish it's execution (i.e. 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 (e.g. for error handling), but sometimes it's OK to use them for event handlers.

like image 144
Sergey Berezovskiy Avatar answered Nov 05 '22 11:11

Sergey Berezovskiy


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.

like image 45
It'sNotALie. Avatar answered Nov 05 '22 10:11

It'sNotALie.