flipchart
flipchart

Reputation: 6578

Is there a way to know if a Task is being awaited?

I have a method

public Task<Task> DoSomeWorkOnARemoteMachine()

which, very descriptively, executes some work on a remote machine by:

  1. Queueing a message on a message bus signalling that work should be done
  2. A remote agent picks up the message and executes the work
  3. The work finishes and the agent queues a message on a message bus to say that the work is done
  4. The application which invoked the work picks up the message that the work is done

The reason I used Task<Task> is because the first Task<> is for the queueing of the message; while the inner Task is completed when the work has been completed on the remote machine (i.e. when the message from the agent has been received). Any exceptions which the remote agent caught during execution of the work are passed along with the completion message and are re-thrown when the inner Task completes.

To call this method I use:

await await DoSomeWorkOnARemoteMachine();

which would wait for the message to be queued to execute the job, as well as to complete the job and receive any exceptions. However, if I was not interested in whether the job completed on the remote agent or not, I might call it as follows:

await DoSomeWorkOnARemoteMachine();

which would not await the inner Task. However, the inner Task (which receives the message from the remote agent, and re-throws the exceptions) would still execute at some point. I feel that this is a bit of a waste and I would like to avoid executing it when I don't await for the results.

My question is thus: is it possible for a Task to "know" whether it is being awaited and not execute if it is not, or execute some other code path (such as an empty Task body).

I do realize that there are alternatives that I could implement, such as passing a "fire and forget" flag, or adding an overload for "fire and forget". It would be great though if I could implement this without the client API changing

Answers involving other projects which implement this sort of remote work execution would also be great!

Upvotes: 10

Views: 259

Answers (3)

svick
svick

Reputation: 245076

I think the way you're doing this is quite confusing. It's not clear at all what the Task<Task> is supposed to mean. What I would do instead is to make your method return something like Task<Work>. The Work type would then have a method like GetResultAsync(), which would return the Task that represents the execution of the work on the remote machine.

This way, you have code that has much clearer meaning and you can also easily recognize whether to process the response, based on whether GetResultAsync() was called or not.

Upvotes: 2

noseratio
noseratio

Reputation: 61744

That is a very interesting question. Yet, if I understood it correctly, I believe this is impossible, in the way you presented it, because it doesn't make sense (IMO). Let's look at the following model (please let me know if I got it wrong):

static async Task<Task> DoSomeWorkOnARemoteMachine()
{
    // Point X: 
    await Task.Delay(1000);
    // Point Y: 
    Console.WriteLine("request sent");
    var taskInner = Task.Delay(2000);
    // Point A: here you want to know if there's an await at Point B
    return taskInner;
}

static async Task Test()
{
    var taskOuter = DoWorkAsync();
    // Point Z: 
    await taskOuter;
    // Point B: 
    await taskOuter.Result; // await taskInner
    Console.WriteLine("request received");
}

At the Point A, you like to know if there's an await at the Point B. But at this moment, the Point B is still in the future, it hasn't happened yet. You'd need a time machine for that :)

[UPDATE] In the same way, at the Point X you cannot know about the await at the Point Z, because the code flow has not reached Z yet.

However, when at Y, in theory you possibly could know about the await at Z (still not about B). Although, I don't know if it's technically possible to get a hold of such information.

Upvotes: 3

Nicolas Voron
Nicolas Voron

Reputation: 3006

I dont think it's easily or efficiently achievable (playing with Task.GetAwaiter is a bad idea, and it will not be accessible from inside your function, see Noseratio's answer). Here is alternative solutions :

Solution n°1 : Function parameter for Task execution

await DoSomeWorkOnARemoteMachine(false) //Indicates that DoSomeWork shouldn't be executed

Solution n°2 : Explicit Task Execution

Task t = await DoSomeWorkOnARemoteMachine() ;
t.Start(); // For example, if you want to execute the resulting Task. 
           // You can also dispatch it to an other thread, etc...

Upvotes: 3

Related Questions