有没有一种方法可以知道任务是否正在被等待?

10
我有一个方法
public Task<Task> DoSomeWorkOnARemoteMachine()

这段代码很形象地描述了如何在远程机器上执行一些工作:

  1. 在消息总线上排队一个消息,表示需要完成工作
  2. 远程代理获取该消息并执行工作
  3. 工作完成后,代理在消息总线上排队一个消息,表示工作已完成
  4. 调用该工作的应用程序获取工作已完成的消息

我使用 Task<Task> 的原因是,第一个 Task<> 用于排队消息;而内部的 Task 在远程机器上的工作完成时完成(即代理接收到消息时)。远程代理在执行工作期间捕获的任何异常都会随着完成消息一起传递,并在内部的 Task 完成时重新抛出。

要调用此方法,可以使用:

await await DoSomeWorkOnARemoteMachine();

如果我不关心工作程序是否在远程代理上完成,我可以按照以下方式调用它:这会等待消息排队以执行任务,以及完成任务并接收任何异常。

await DoSomeWorkOnARemoteMachine();

如何完成一个不会 await 内部 Task 的函数,但是内部的 Task (负责接收来自远程代理的消息并重新抛出异常)仍然会在某个时刻执行。我认为这有点浪费,希望在没有等待结果时能避免其执行。

我的问题是:是否可能让一个Task“知道”它是否被await并在未被await时不执行或者执行其他代码路径(比如空的Task体)。

我确实意识到可以采用其他方法来实现此目的,例如传递“fire and forget”标志或添加“fire and forget”重载。但如果能够在不更改客户端API的情况下实现这一点,那将是很好的。

涉及实现此类远程工作执行的其他项目的答案也非常棒!


1
你几乎肯定不会想这样做,即使你能够这样做。如果将延续添加到该任务中而不是立即添加,那么它们现在永远不会运行,因为任务永远不会完成。将任务插入数据结构或字段中,然后稍后添加延续通常是相当普遍的做法;这将导致它们全部中断,但是以一种非常令人困惑和难以调试的方式。 - Servy
3个回答

3
我认为这并不容易或高效实现(使用Task.GetAwaiter可能是一个坏主意,并且它将无法从函数内部访问,请参见Noseratio的答案)。以下是替代解决方案:
解决方案1:使用函数参数执行Task。
await DoSomeWorkOnARemoteMachine(false) //Indicates that DoSomeWork shouldn't be executed

解决方案二:显式任务执行
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...

2
+1,像这样的“bool”参数是指示外部任务逻辑不启动内部任务并返回“Task.FromResult”(或“null”)的自然方式。 - noseratio - open to work
一个必须要调用 Start() 方法才能运行的 Task 并不是很有意义,特别是因为这意味着它将会阻塞。 - svick

3

这是一个非常有趣的问题。然而,如果我理解正确的话,我认为在你提出的方式下,这是不可能的,因为它没有意义(依我之见)。让我们看看下面的模型(如果我理解错误,请告诉我):

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");
}

A点,您想知道B点是否有await。但此时,B点仍在未来,尚未发生。你需要一台时间机器来做到这一点 :) [更新]同样地,在X点,您无法得知Z点await,因为代码流尚未到达Z点
然而,在Y点,理论上您可能会知道Z点await(但还不知道B点)。虽然,我不知道是否技术上可能获得这样的信息。

2
我认为你现在的做法很令人困惑。根本不清楚Task<Task>表示什么意思。我建议您改成返回Task<Work>类型的数据。然后,Work类型会有一个名为GetResultAsync()的方法,该方法返回代表在远程机器上执行工作的Task
这样,您的代码含义更加明确,还可以根据是否调用了GetResultAsync()来轻松识别是否要处理响应。

我实际上非常喜欢那种方法。它更加简洁。 - flipchart
最终我选择了这种方法。虽然其他答案也有所帮助,但这个答案给了我很好的方向,我非常感激。 - flipchart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接