.NET Async / Await - когда возвращать задачу и когда ждать - PullRequest
1 голос
/ 07 марта 2019

Я нахожусь в процессе преобразования синхронного кода в асинхронный код. У меня есть приложение Windows Form на VB.net, которое показывает представление физических устройств, которые являются частью регистрационного поста, управляемого почтовым менеджером.

Таким образом, класс PostManager используется для управления объектами RegistrationPost. Эти регистрационные посты содержат несколько устройств, которые так или иначе должны быть связаны с Socket коммуникацией. Эти устройства соответствуют интерфейсу IDevice путем реализации подпрограммы «connect».

Ниже приведена сокращенная версия кода, чтобы понять, что я пытаюсь сделать:

Private Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Try
        ...
        Dim pm As New PostManager
        pm.ConnectAndStartPosts
        ...
    Catch ex As Exception
        ...
    End Try
End Sub

Затем внутри класса PostManager:

Friend Sub ConnectAndStartPosts()
    ' The global variable "Posts" is a List of RegistrationPost objects
    Parallel.ForEach(Of RegistrationPost)(Posts, Sub(x) x.ConnectAndStartReading())
End Sub

Тогда внутри класса RegistrationPost:

Public Sub ConnectAndStartReading()
    Parallel.ForEach(Of IDevice)(AllDevices, Sub(x)
                                                 x.Connect()
                                                 ...
                                             End Sub)
End Sub

Тогда внутри IDevice:

Public Sub Connect() Implements IDevice.Connect
    ' socket is a global variable of the type Socket(SocketType.Stream, ProtocolType.Tcp)
    socket.Connect(IP, Port)
    ... more code gets executed (if connected, log a message)

    NotifyPropertyChanged("Connected")
End Sub

Хорошо, все это работает, но, конечно, я хотел бы использовать async / await, чтобы получить все преимущества асинхронного подключения к внешнему сокету. Но у меня возникли проблемы с пониманием того, как далеко "всплывают" ключевые слова асинхронного ожидания Боюсь, что возникнут проблемы с производительностью, потому что каждый раз, когда вы используете ключевое слово async / await, создается конечный автомат, так что, вероятно, нет необходимости идти до конца?

Было бы также лучше отказаться от Parallel.Foreach и перейти на обычный ForEach? Или даже другой подход? Ниже показано, как это повлияет на пример кода:

' Async/await here as well?
Private async Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Try
        ...
        Dim pm As New PostManager
        await pm.ConnectAndStartPosts
        ...
    Catch ex As Exception
        ...
    End Try
End Sub

Затем внутри класса PostManager:

' Leave this a regular 'Sub' or transform to async Function as Task?
Friend Sub ConnectAndStartPosts()
    ' The global variable "Posts" is a List of RegistrationPost objects
    Parallel.ForEach(Of RegistrationPost)(Posts, async Sub(x) await x.ConnectAndStartReading())
End Sub

Тогда внутри класса RegistrationPost:

' Leave this a regular 'Sub' or transform to async Function as Task?
Public async Function ConnectAndStartReading() as Task
    Parallel.ForEach(Of IDevice)(AllDevices, async Sub(x)
                                                 await x.Connect()
                                                 ... more code, start reading, etc...
                                             End Sub)
End Function

Тогда внутри IDevice:

Public async Function Connect() as Task Implements IDevice.Connect
    ' socket is a global variable of the type Socket(SocketType.Stream, ProtocolType.Tcp)
    await socket.ConnectAsync(IP, Port)
    ... more code gets executed (if connected, log a message)

    NotifyPropertyChanged("Connected")
End Function

Ответы [ 2 ]

0 голосов
/ 25 апреля 2019

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

  1. Async Form_load запускает "await postManager.ConnectAndStartPostsAsync".
  2. postManager.ConnectAndStartPostsAsync:

    Dim tasks As IEnumerable (Of Task) = Из сообщения В сообщениях Выберите post.ConnectAndStartReadingAsync Await Task.WhenAll (tasks.ToArray)

  3. Теперь в регистрационном посте мне нужно синхронно запустить две вещи в одной единице работы (задании): а) жду подключения б) дождаться начала чтения

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

Итак, на шаге 3 я ищу способ: дождаться Task.WhenAll (collectionOfTasks), где каждая отдельная задача выполняет две задачи внутри задачи.

0 голосов
/ 07 марта 2019

У меня проблемы с пониманием того, как далеко "всплывают" ключевые слова async / await? Боюсь, что возникнут проблемы с производительностью, потому что каждый раз, когда вы используете ключевое слово async / await, создается конечный автомат, так что, вероятно, не нужно идти до конца?

Вы должны пройти async до конца . Затраты на async / await, в то время как не ноль , полностью теряются в шуме, как только вы выполняете реальный ввод / вывод.

Было бы также лучше отказаться от Parallel.Foreach и перейти на обычный ForEach? Или даже другой подход?

Да. Parallel предназначен для кода, связанного с процессором. Для асинхронного параллелизма используйте Task.WhenAll. То есть, вызовите свой новый метод ConnectAsync для каждого устройства (например, используя Select LINQ), а затем выполните Await Task.WhenAll для получающихся задач. Вы можете сделать то же самое для регистрации сообщений.

...