Должен ли я использовать await внутри моего Task.Run ()? - PullRequest
1 голос
/ 16 апреля 2020

** Я суммировал этот вопрос внизу с правкой **

Это было задано ранее, но я думаю, что мои обстоятельства другие.

Я обрабатываю несколько запросов одновременно .

Это мой код для этого, он запускается в al oop. Я удалил немного кода, который обрабатывает переменную taskAllocated для краткости.

 while (!taskAllocated)
 {
     lock (_lock)
     {
         // Find an empty slot in the task queue to insert this task
         for (i = 0; i < MaxNumTasks; i++)
         {
             if (_taskQueue[i] == null)
             {
                 _taskQueue[i] = Task.Run(() => Process());
                 _taskQueue[i].ContinueWith(ProcessCompleted);
                 break;
             }
         }
     }
  }

Процесс - это типичный async Task Process() { CpuIntensiveStuff(); } метод.

Я выполняю приведенный выше код, и это работало нормально. Это многопоточность приятно. Всякий раз, когда элемент входит, он найдет пустой слот в очереди задач и удалит его. Когда задача завершается, запускается метод ProcessCompleted и освобождается слот.

Но потом я подумал: а не следует ли использовать await внутри моего Task.Run? Что-то вроде:

_taskQueue[i] = Task.Run(async () => await Process());

Подумав об этом, я не уверен. ContinueWith срабатывает правильно, когда задача завершена, поэтому, возможно, в этом нет необходимости.

Я спрашиваю, потому что хотел отслеживать и регистрировать, сколько времени занимает выполнение каждой задачи.

Поэтому вместо Process () я бы сделал другой метод, например:

async Task DoProcess() 
{ 
    var sw = Stopwatch.StartNew();
    Process();
    sw.Stop();
    Log(sw.ElapsedMilliseconds);
}

И мне пришло в голову, что если я это сделаю, я не был уверен, нужно ли мне await Process(); или нет, в дополнение к тому, что я не знал, стоит ли мне ждать внутри Task.Run()

Я немного ошарашен по этому поводу. Кто-нибудь может предложить руководство?


Редактировать:

Подводя итог:

Если сомометод:

void SomeMethod() { }

Тогда

Task.Run(() => SomeMethod()); отлично, вызывает SomeMethod в новом «потоке» (не технически, но вы понимаете, что я имею в виду).

Однако мой SomeMethod на самом деле:

async Task SomeMethod() { }

Вам нужно сделать что-то особенное с Task.Run()?

Мой код, я не являюсь, я просто прямо игнорирую, что это асин c Задача, и это, кажется, работает:

Task.Run(() => SomeMethod()); // SomeMethod is async Task but I am ignoring that

Но я не уверен, что это а) должно работать или б) хорошая идея. Альтернативой может быть:

Task.Run(async() => await SomeMethod());

Но есть ли смысл? И это усугубляется тем фактом, что я действительно хочу сделать:

Task.Run(() => 
{ 
    someCode(); 
    var x = startTimer();
    SomeMethod(); 
    var y = stopTimer();
    someMoreCode()
}); 

, но без ожидания я не уверен, что он будет ждать, пока некий метод завершится sh и таймер будет неправильным.

1 Ответ

3 голосов
/ 16 апреля 2020

Все становится более понятным, если вы не используете анонимные методы. Например,

Task.Run(() => Process())

эквивалентно этому:

Task.Run(DoSomething);

Task DoSomething() {
    return Process();
}

, тогда как

Task.Run(async () => await Process())

эквивалентно этому:

Task.Run(DoSomething);

async Task DoSomething() {
    await Process();
}

В большинстве случаев нет никакой функциональной разницы между return SomethingThatReturnsATask() и return await SomethingThatReturnsATask(), и вы обычно хотите вернуть Task напрямую и не использовать await (по причинам, описанным здесь ). При использовании внутри Task.Run все может легко go плохо, если у команды. NET нет вашей спины.

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

Это могло бы означать , что Task, возвращаемый из Task.Run, будет завершаться всякий раз, когда Process() возвращает Task. И поскольку Process() возвращает Task при первом await, это произойдет, когда еще не полностью завершено .

. Команда NET имеет ваша спина

Однако это не так, потому что Task.Run имеет перегрузку speci c, когда вы даете ему метод, возвращающий Task. И если вы посмотрите на код , он вернет Task *, который привязан к возвращаемому вами Task.

Это означает, что Task вернулось из Task.Run(() => Process()) не завершится до тех пор, пока не будет завершено Task, возвращенное из Process().

Таким образом, ваш код в порядке.

...