Есть ли способ принудительно переключить контекст без задачи.Задержка в асинхронном коде

#c# #asynchronous #async-await

Вопрос:

У меня есть код, который выглядит примерно так:

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

Однако он не выполняет переключение контекста (и будет выполняться вечно), если я не вставлю задачу.Задержка в цикле

Есть ли лучший способ добиться этого (без Task.Run)?

 var tasks = new List<Task>();
var cts = new CancellationTokenSource();
tasks.Add(DoSomething(cts.Token));
cts.Cancel();
Task.WaitAll(tasks.ToArray());
Console.WriteLine("Done");

async Task DoSomething(CancellationToken ct)
{
    while (!ct.IsCancellationRequested)
    {
        await Task.CompletedTask;
        await Task.Delay(1); // Without this is doesn't do context switch
    }
}
 

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

Комментарии:

1. В чем проблема с использованием Task.Delay ?

2. попробуйте Task. Выход?

3. Задача. Выход идеальный! Спасибо, я не знал об этом. Вы можете опубликовать в качестве ответа, если хотите, я приму его

4. async..await само по себе не заставляет код выполняться асинхронно. Это позволяет сделать это только элегантным и прозрачным способом. Таким образом, асинхронный метод всегда будет выполняться синхронно до момента, когда вы принудительно переключаете контекст (например, используя await Task.Run(...) await Task.Yield или ожидая другого асинхронного метода с асинхронным кодом).

5. @RedRidingHood добавил ответ

Ответ №1:

Попробуйте

 async Task DoSomething(CancellationToken ct)
{
    while (!ct.IsCancellationRequested)
    {
        //await Task.CompletedTask; //you can omit this line
        await Task.Yield();
    }
}
 

Ответ №2:

Если вы хотите имитировать отменяемую операцию, самый простой способ, вероятно, таков:

 Task DoSomething(CancellationToken cancellationToken)
{
    return Task.Delay(Timeout.Infinite, cancellationToken);
}
 

cancellationToken Параметр имеет отменяющую семантику, что означает, что при отмене токена Task он перейдет в Canceled состояние. Если вы хотите, чтобы у него была семантика остановки, которая является нестандартной семантикой для a CancellationToken , вы могли бы сделать это:

 async Task DoSomething(CancellationToken stoppingToken)
{
    try { await Task.Delay(Timeout.Infinite, stoppingToken); }
    catch (OperationCanceledException) { }
}
 

Теперь, когда токен отменяется, Task он переходит в RanToCompletion состояние. Имейте в виду, что крайне редко можно увидеть CancellationToken параметр с семантикой остановки в стандартных библиотеках .NET.