Почему мой код работает в нескольких потоках? - PullRequest
0 голосов
/ 23 июня 2019

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

Вот мое заявление:

namespace ConsoleApp3
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            var work1 = new WorkClass();
            var work2 = new WorkClass();

            while(true)
            {
                work1.DoWork(500);
                work2.DoWork(1500);
            }
        }
    }

    public class WorkClass
    {
        public async Task DoWork(int delayMs)
        {
            var x = 1;

            await Task.Delay(delayMs)

            var y = 2;
        }
    }
}

Это просто пример, который я создал, чтобы проверить, как будет выполняться код. Есть несколько вещей, которые меня удивляют. Во-первых, здесь много тем! Если я установлю точку останова на var y = 2;, я увижу, что threadId там не тот, это может быть 1, или 5, или 6, или что-то еще. Это почему? Я подумал, что async / await не использует дополнительные потоки сам по себе, если только я явно не командую этим (с помощью Task.Run или создания нового потока). По крайней мере эта статья пытается сказать, что я думаю.

Хорошо, но допустим, что по какой-то причине существуют некоторые другие потоки - даже если они есть, у моего await Task.Delay(msDelay); нет ConfigureAwait(false)! Насколько я понимаю, без этого вызова поток не должен меняться.

Мне действительно трудно хорошо понять концепцию, потому что я не могу найти ни одного хорошего ресурса, который содержал бы всю информацию вместо нескольких частей информации.

Ответы [ 2 ]

9 голосов
/ 23 июня 2019

Когда асинхронный метод ожидает чего-то, если он не завершен, он планирует продолжение, а затем возвращает.Вопрос в том, на каком потоке продолжается продолжение.Если есть контекст синхронизации, продолжение планируется запустить в этом контексте - обычно это поток пользовательского интерфейса или, возможно, определенный пул потоков.

В вашем случае вы запускаете консольное приложение, что означает: равно без контекста синхронизации (SynchronizationContext.Current вернет ноль).В этом случае продолжения выполняются в потоках пула потоков.Дело не в том, что специально создан новый поток для запуска продолжения - просто пул потоков подберет продолжение, тогда как «основной» поток не будет запускать продолжение.

ConfigureAwait(false) используетсячтобы указать, что вы не хотите возвращаться к текущему контексту синхронизации для продолжения, но поскольку в вашем случае контекст синхронизации в любом случае отсутствует, это не имеет значения.

1 голос
/ 23 июня 2019

Async / await не использует дополнительные потоки самостоятельно, но в вашем примере это не само по себе. Вы вызываете Task.Delay, и этот метод планирует продолжение для запуска в потоке пула потоков. Во время задержки поток не заблокирован. Новая тема не создана. Когда наступит время, существующий поток будет использован для запуска продолжения, что в вашем случае требует очень мало работы (просто выполните назначение var y = 2), потому что вы даже не ожидаете задачу, возвращаемую DoWork. Когда эта работа будет выполнена (через доли микросекунды), поток пула потоков снова будет свободен для выполнения других заданий.

Вместо Task.Delay вы можете await другой метод, который вообще не использует потоки, или метод, который создает выделенный длительный поток, или метод, который запускает новый процесс. Async / await не несет ответственности за любой из них. Async / await - это всего лишь механизм создания продолжений задач в удобной для разработчика форме.

Вот ваше приложение, изменённое для мира без асинхронности / ожидания:

class Program
{
    static Task Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        var work1 = new WorkClass();
        var work2 = new WorkClass();

        while (true)
        {
            work1.DoWork(500);
            work2.DoWork(1500);
        }
    }
}

public class WorkClass
{
    public Task DoWork(int delayMs)
    {
        var x = 1;

        int y;

        return Task.Delay(delayMs).ContinueWith(_ =>
        {
            y = 2;
        });
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...