Есть ли способ узнать, ожидает ли задание?

У меня есть метод

public Task<Task> DoSomeWorkOnARemoteMachine()

который очень описательно выполняет некоторую работу на удаленном компьютере с помощью:

  • Очередь сообщения на шине сообщений, сигнализирующая о том, что работа должна выполняться
  • Удаленный агент берет сообщение и выполняет работу.
  • Работа завершается, и агент ставит в очередь сообщение на шине сообщений, чтобы сказать, что работа выполнена
  • Приложение, вызвавшее работу, забирает сообщение о том, что работа выполнена

Причина, по которой я использовал Task<Task>, состоит в том, что первая Task<> предназначена для очередности сообщения; а внутренний Task завершается, когда работа завершена на удаленной машине (то есть когда сообщение от агента получено). Любые исключения, которые удаленный агент, захваченный во время выполнения работы, передаются вместе с сообщением о завершении и повторно запускаются, когда внутренний Task завершается.

Чтобы вызвать этот метод, я использую:

await await DoSomeWorkOnARemoteMachine();

который будет ждать, пока сообщение будет поставлено в очередь для выполнения задания, а также для завершения задания и получения каких-либо исключений. Однако, если меня не интересовало, выполнено ли задание удаленному агенту или нет, я могу назвать его следующим:

await DoSomeWorkOnARemoteMachine();

который не был бы await внутренним Task. Однако внутренний Task (который получает сообщение от удаленного агента и повторно выбрасывает исключения) все равно выполнит в какой-то момент. Я чувствую, что это немного пустая трата, и я бы хотел избежать ее выполнения, когда я не await для результатов.

Таким образом, мой вопрос: возможно ли Task "знать", является ли оно await ed и не выполняется, если это не так, или выполнить какой-либо другой путь кода (например, пустой Task тела).

Я понимаю, что есть альтернативы, которые я мог бы реализовать, например, передать флаг "огонь и забыть" или добавить перегрузку для "огня и забыть". Было бы здорово, если бы я мог реализовать это без изменения API-интерфейса клиента

Ответы, связанные с другими проектами, которые реализуют такое удаленное выполнение работы, также будут большими!

Ответ 1

Я думаю, что вы делаете это довольно запутанно. Не совсем ясно, что означает Task<Task>. Вместо этого я сделаю так, чтобы ваш метод возвращал что-то вроде Task<Work>. Тогда тип Work имел бы такой метод, как GetResultAsync(), который вернул бы Task, который представляет выполнение работы на удаленной машине.

Таким образом, у вас есть код, который имеет гораздо более четкое значение, и вы также можете легко узнать, обрабатывать ли ответ, основываясь на том, был ли вызван GetResultAsync().

Ответ 2

Это очень интересный вопрос. Однако, если я правильно понял это, я считаю, что это невозможно, так, как вы его представляли, потому что это не имеет смысла (IMO). Давайте посмотрим на следующую модель (пожалуйста, дайте мне знать, если я ошибаюсь):

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 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 вам нравится знать, существует ли await в Точке B. Но в данный момент Point B все еще в будущем, этого еще не произошло. Для этого вам понадобится машина времени:)

[UPDATE] Точно так же в Точке X вы не можете узнать о await в Точке Z, потому что поток кода еще не достиг Z.

Однако, когда у Y, теоретически вы могли бы узнать о await в Z (все еще не о B). Хотя, я не знаю, технически ли можно получить такую ​​информацию.

Ответ 3

Я не думаю, что это легко или эффективно достижимо (игра с Task.GetAwaiter - плохая идея, и она не будет доступна изнутри вашей функции, см. ответ Noseratio). Вот альтернативные решения:

Решение n ° 1: параметр функции для Task выполнения

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

Решение № 2: Явное выполнение задачи

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