syncis
syncis

Reputation: 603

Task.Factory.New vs Task.Run vs async/await, best way to not block UI thread

I need to make sure some of my code runs in the background and doesn't stop block the UI thread, and I thought I knew how to use Task.Factory.New, but as I read more and more, it seems that it doesn't always create a new thread but sometimes runs it on the UI thread if the thread pool thinks that's the way to go so now I am pretty confused over what to use to be safe that the method is not blocking the UI thread. I should also add that I don't need a return value from the method , I just need it to run asynchronously ( in the background ).

So what's the difference between these methods below and what is best to use to make sure it doesn't block the UI thread ?

And is it really true that Task.Factory.StartNew doesn't always create a thread that runs in the background and doesn't block the UI?

     public Form1()
    {
        Task.Factory.StartNew(() => Thread.Sleep(1000));

        Task.Run(() => Thread.Sleep(1000));

        Task<int> longRunningTask = LongRunningOperationAsync();
    }

    public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
    {
        await Task.Delay(1000);
        return 1;
    }

Upvotes: 2

Views: 1697

Answers (3)

Stephen Cleary
Stephen Cleary

Reputation: 456527

I should also add that I don't need a return value from the method

Beware of a common problem: just because you don't need a return value doesn't mean you shouldn't await the returned task. awaiting the task provides you with exception information as well. The only time it's appropriate to ignore the returned task is when you don't care about the return value and you don't care about any exceptions the background work may raise (that is, you're OK with silently swallowing them).

In easily >99% of cases, the appropriate behavior is to await the task.

So what's the difference between these methods below

StartNew is an outdated way to run code on a thread pool thread. You should use Run instead of StartNew. I describe why in painful detail on my blog, but the short answer is that StartNew does not understand async delegates and has inappropriate default options.

async is completely different. Run executes code on a thread pool thread, but async rewrites the code so that it doesn't need a thread while there's an asynchronous operation in progress. So, the Run example will block a thread pool thread for 1000ms in the Thread.Sleep call. The async example will run on the UI thread and then not use the UI thread for 1000ms in the await Task.Delay call.

and what is best to use to make sure it doesn't block the UI thread?

Both Task.Run and async are appropriate in UI apps, depending on the nature of the background code.

If your code is asynchronous (usually all I/O-bound code), then you should use async/await. Note that imposing asynchrony "top-down" is hard. Don't think about "forcing code off the UI thread"; instead, identify the naturally-asynchronous parts of your system (DB, file, WebAPI calls, etc), change them to be async at the lowest level, and work up from there.

If your code is synchronous (CPU-bound), then you can use Task.Run if it takes too long to run on your UI thread.

And is it really true that Task.Factory.StartNew doesn't always create a thread that runs in the background and doesn't block the UI?

Run always executes code on threads from the thread pool. The rules for StartNew are much more complex, but in your example example code, if TaskScheduler.Current == TaskScheduler.Default, then StartNew will also use a thread from the thread pool.

Upvotes: 2

Tim Bee
Tim Bee

Reputation: 2230

The two examples you give are pretty much the same and depend of the context you are using them on.

Tasks are a way to describe a packet of work with meta information to know about its state, synchronize multiple tasks, cancel them etc. The main idea about tasks and asynchronicity is that you don't have to care about "where" (on which thread) this unit of work is executed, it is performed asynchronously, that's all you need to know. So no worries for your UI-Thread, as long as you are working asynchronously, it won't be locked (that's one of the main improvements since Win8 and the async/await keywords, every UI Manipulation has to be async not to bloat the UI).

As for async&await, those are in fact syntactic sugar. In many simple cases, you don't need to care about the task itself, you just want the asynchronous computation "do this and give me the answer when you have it".

int x = await ComputeXAsync(); //Returns a Task<int> which will be automatically unwrapped as an int 

If you need to synchronize tasks, for example start multiple tasks in parallel and wait that all of them are done before continuing, you can't rely on the simple await Task<int> format, you then need to work with the task itself, its Status property and in this case the Task.WaitAll(task[]) API.

Edit: I really recommend the chapter of JonSkeets Book C# in Depth (last edition). The Async/Await part is really well explained and will take you way further than any reading online will do.

Upvotes: 0

Joerg
Joerg

Reputation: 131

The first two are actually the same. see here

Well you can think of it as a task is something you want to be done. A thread is something like a worker which does the task for you. So basically a threads helps you doing a task, but it doesn't return a value.

a task does not create its own OS thread. Instead, tasks are executed by a TaskScheduler; the default scheduler simply runs on the ThreadPool.

See Here

An example is. A remote request you can do as task, you want the job done but dont know when it will be. But a TCPListener for the server I would run as a thread, because it always needs to listen to the socket.

Like Andreas Niedermair pointed out, you can also use longliving tasks with the TaskCreationOptions

Upvotes: 0

Related Questions