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

.net, c#

Solution

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.)

Problem

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.

Original source