Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to signal a System.Threading.Tasks.Task complete?

Tags:

c#

.net

I have a library that is a complicated arbiter of many network connections. Each method of it's primary object takes a delegate, which is called when the network responds to a given request.

I want to translate my library to use the new .NET 4.5 "async/await" pattern; this would require me to return a "Task" object, which would signal to the user that the asynchronous part of the call is complete. Creating this object requires a function for the task to represent - As far as my understanding, it is essentially a lightweight thread.

This doesn't really fit the design of my library - I would like the task to behave more like an event, and directly signal to the user that their request has completed, rather then representing a function. Is this possible? Should i avoid abusing the "async/await" pattern in this way?

I don't know if I'm wording this very well, I hope you understand my meaning. Thank you for any help.

like image 880
Woodrow Douglass Avatar asked Nov 21 '12 15:11

Woodrow Douglass


People also ask

What is using system threading tasks?

Provides types that simplify the work of writing concurrent and asynchronous code. The main types are Task which represents an asynchronous operation that can be waited on and cancelled, and Task<TResult>, which is a task that can return a value.

Are tasks and threads the same?

A task can have multiple processes happening at the same time. Threads can only have one task running at a time. We can easily implement Asynchronous using 'async' and 'await' keywords. 6.

Why are tasks better than threads?

Task is more abstract then threads. It is always advised to use tasks instead of thread as it is created on the thread pool which has already system created threads to improve the performance. The task can return a result. There is no direct mechanism to return the result from a thread.

What is task yield ()?

You can use await Task. Yield(); in an asynchronous method to force the method to complete asynchronously. If there is a current synchronization context (SynchronizationContext object), this will post the remainder of the method's execution back to that context.


2 Answers

As far as my understanding, it is essentially a lightweight thread.

No, that's not really true. I can be true, under certain circumstances, but that's just one usage of Task. You can start a thread by passing it a delegate, and having it execute it (usually asynchronously, possibly synchronously, and by default using the thread pool).

Another way of using threads is through the use of a TaskCompletionSource. When you do that the task is (potentially) not creating any threads, using the thread pool, or anything along those lines. One common usage of this model is converting an event-based API to a Task-based API:

Let's assume, just because it's a common example, that we want to have a Task that will be completed when a From that we have is closed. There is already a FormClosed event that fires when that event occurs:

public static Task WhenClosed(this Form form)
{
    var tcs = new TaskCompletionSource<object>();

    form.FormClosing += (_, args) =>
    {
        tcs.SetResult(null);
    };

    return tcs.Task;
}

We create a TaskCompletionSource, add a handler to the event in question, in that handler we signal the completion of the task, and the TaskCompletionSource provides us with a Task to return to the caller. This Task will not have resulted in any new threads being created, it won't use the thread pool, or anything like that.

You can have a Task/Event based model using this construct that appears quite asynchronous, but only using a single thread to do all work (the UI thread).

In general, anytime you want to have a Task that represents something other than the execution of a function you'll want to consider using a TaskCompletionSource. It's usually the appropriate conceptual way to approach the problem, other than possibly using one of the existing TPL methods, such as WhenAll, WhenAny, etc.

Should I avoid abusing the "async/await" pattern in this way?

No, because it's not abuse. It's a perfectly appropriate use of Task constructs, as well as async/await. Consider, for example, the code that you can write using the helper method that I have above:

private async void button1_Click(object sender, EventArgs e)
{
    Form2 popup = new Form2();
    this.Hide();
    popup.Show();
    await popup.WhenClosed();
    this.Show();
}

This code now works just like it reads; create a new form, hide myself, show the popup, wait until the popup is closed, and then show myself again. However, since it's not a blocking wait the UI thread isn't blocked. We also don't need to bother with events; adding handlers, dealing with multiple contexts; moving our logic around, or any of it. (All of that happens, it's just hidden from us.)

like image 79
Servy Avatar answered Sep 28 '22 16:09

Servy


I want to translate my library to use the new .NET 4.5 "async/await" pattern; this would require me to return a "Task" object, which would signal to the user that the asynchronous part of the call is complete.

Well, not really - you can return anything which implements the awaitable pattern - but a Task is the simplest way of doing this.

This doesn't really fit the design of my library - I would like the task to behave more like an event, and directly signal to the user that their request has completed, rather then representing a function.

You can call Task.ContinueWith to act as a "handler" to execute when the task completes. Indeed, that's what TaskAwaiter does under the hood.

Your question isn't terribly clear to be honest, but if you really want to create a Task which you can then force to completion whenever you like, I suspect you just want TaskCompletionSource<TResult> - you call the SetResult, SetCanceled or SetException methods to indicate the appropriate kind of completion. (Or you can call the TrySet... versions.) Use the Task property to return a task to whatever needs it.

like image 38
Jon Skeet Avatar answered Sep 28 '22 16:09

Jon Skeet