.NET Async / Await-返回任务时和等待时

时间:2019-03-07 15:42:27

标签: .net vb.net asynchronous async-await

我正在将同步代码转换为异步代码。我在VB.net中有一个Windows窗体应用程序,该应用程序显示了物理设备的表示形式,这些物理设备是由后期管理员管理的注册帖子的一部分。

因此PostManager类用于操作RegistrationPost对象。这些注册站包含需要以一种或另一种方式与Socket通信连接的多个设备。这些设备通过实现“连接”子功能来符合IDevice接口。

在代码的精简版本下面,以了解我要执行的操作:

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 :(得分:0)

  

我无法理解将“异步/等待”关键字“弄混”到什么程度?我担心会遇到性能问题,因为每次您使用async / await关键字时,都会创建一个状态机,因此可能不需要一直使用它吗?

您应该去async all the way。当您进行真正的I / O操作时,async / await的开销(尽管not zero)就完全消失了。

  

丢弃Parallel.Foreach并定期使用ForEach会更好吗?甚至是另一种方法?

是的。 Parallel适用于CPU绑定代码。对于异步并发,请使用Task.WhenAll。也就是说,为每个设备调用新的ConnectAsync方法(例如,使用LINQ的Select),然后对产生的任务执行Await Task.WhenAll。您可以对注册信息执行相同的操作。

答案 1 :(得分:0)

我更改了代码,以反映所选答案中的建议。我不确定是否要提出一个新问题,但是我现在在这里:

  1. 异步Form_load运行“等待postManager.ConnectAndStartPostsAsync”。
  2. postManager.ConnectAndStartPostsAsync:

    将任务作为IEnumerable(Of Task)=从帖子中发帖选择帖子post.ConnectAndStartReadingAsync 等待Task.WhenAll(tasks.ToArray)

  3. 现在在注册站中,我需要在一个工作单元(任务)中同步运行两件事: a)等待连接 b)等待开始阅读

但是对于一组注册帖子,这需要异步完成。我不确定如何在异步运行任务本身的同时,获取一组注册信息以同步连接并开始阅读。

因此,在第3步中,我正在寻找一种方法:等待Task.WhenAll(collectionOfTasks),其中每个任务在任务内部执行两次等待。