Чтобы расширить мои комментарии под вопросом
Я все же рекомендую вам следовать рекомендациям, чтобы избежать блокировки вызовов .
Но сейчас давайте предположим, что вы действительно не можете, тогда вам следует хотя бы разгрузить вычисления из потока всякий раз, когда это необходимо, чтобы дать процессору livecheck .
Рассмотрим следующий пример:
void Computation()
{
for (var i = 0; i < 300; i++)
{
Thread.Sleep(1);
}
}
void LiveCheck()
{
Console.WriteLine("I'm alive.");
}
async Task Main()
{
var tasks = new List<Task>();
// Create 1000 blocking compuations to simulate busy thread pool
// or thread pool starvation
for (int i = 0; i < 1000; i++)
{
tasks.Add(Task.Run(Computation));
}
// Simulate 3 seconds after thread pool is busy, execute livecheck
Thread.Sleep(3000);
var sw = Stopwatch.StartNew();
await Task.Run(LiveCheck);
sw.Stop();
Console.WriteLine($"LiveCheck completed in {sw.Elapsed.TotalSeconds} seconds");
await Task.WhenAll(tasks);
}
В большинстве случаев возвращаемый результат на моей машине выглядит следующим образом:
LiveCheck completed in 31.2030817 seconds
Если выгрузить вычисление из потока:
async Task Computation()
{
for (var i = 0; i < 300; i++)
{
Thread.Sleep(1);
// Yield thread every 50ms
if ((i % 50) == 0)
{
await Task.Yield();
}
}
}
Выходные данные обычно таковы:
LiveCheck completed in 2.4753268 seconds
Компромисс в данном случае один прогон вычисления будет медленнее, чем версия syn c.