Задача не ждет в "WaitAll" - PullRequest
0 голосов
/ 05 июля 2018

Я столкнулся с проблемой, когда «WaitAll» для моих задач не ожидает завершения всех задач. Это проект VB с сабами и функциями. Многие другие примеры этого (в C #) используют лямбда-выражения, которые нелегко конвертировать в VB.

Я протестировал образец программы и заставил ее работать, но как только я пытаюсь установить связь с веб-службой (которая добавляет асинхронный вызов и ожидание), он больше не ждет в WaitAll.

Это пример файла журнала. Вы можете увидеть, как нажатие кнопки заканчивается в строках 6 и 7, даже если задачи все еще выполняются и продолжают регистрировать работу после завершения нажатия кнопки.

2018-07-05 12:18:38.1481 |  INFO | Form1.btnRun_Click | ############################ 
2018-07-05 12:18:38.1790 |  INFO | Form1.btnRun_Click | Run button clicked 
2018-07-05 12:18:38.2129 |  INFO | ProcessOneChunkOfPeopleData | Thread #1 contains 2 people 
2018-07-05 12:18:38.2129 |  INFO | ProcessOneChunkOfPeopleData | Thread #2 contains 2 people 
2018-07-05 12:18:38.2129 |  INFO | ProcessOneChunkOfPeopleData | Thread #3 contains 2 people 
2018-07-05 12:18:38.2980 |  INFO | Form1.btnRun_Click | Run button code has completed 
2018-07-05 12:18:38.2980 |  INFO | Form1.btnRun_Click | ############################ 
2018-07-05 12:18:38.3788 |  INFO | ProcessOneChunkOfPeopleData | Thread 1. Finished processing person Peter (1 of 2) 
2018-07-05 12:18:38.3788 |  INFO | ProcessOneChunkOfPeopleData | Thread 3. Finished processing person Dave (1 of 2) 
2018-07-05 12:18:38.3947 |  INFO | ProcessOneChunkOfPeopleData | Thread 2. Finished processing person Chantal (1 of 2) 
2018-07-05 12:18:38.4665 |  INFO | ProcessOneChunkOfPeopleData | Thread 3. Finished processing person Helen (2 of 2) 
2018-07-05 12:18:38.4695 |  INFO | ProcessOneChunkOfPeopleData | Thread #3 has finished processing all people 
2018-07-05 12:18:38.4948 |  INFO | ProcessOneChunkOfPeopleData | Thread 2. Finished processing person Tammy (2 of 2) 
2018-07-05 12:18:38.5018 |  INFO | ProcessOneChunkOfPeopleData | Thread #2 has finished processing all people 
2018-07-05 12:18:38.4948 |  INFO | ProcessOneChunkOfPeopleData | Thread 1. Finished processing person Jeff (2 of 2) 
2018-07-05 12:18:38.5157 |  INFO | ProcessOneChunkOfPeopleData | Thread #1 has finished processing all people 

Вот код, с которым я тестировал. Можете ли вы объяснить, почему WaitAll не ждет?

Imports System.Net.Http
Imports NLog

Public Class Form1

    Private m_Logger As Logger = LogManager.GetCurrentClassLogger

    Private Sub btnRun_Click(sender As Object, e As EventArgs) Handles btnRun.Click
        m_Logger.Info("############################")
        m_Logger.Info("Run button clicked")

        'Create a bunch of people
        Dim Peter As New Person(1, "Peter")
        Dim Jeff As New Person(2, "Jeff")
        Dim Chantal As New Person(3, "Chantal")
        Dim Tammy As New Person(4, "Tammy")
        Dim Dave As New Person(5, "Dave")
        Dim Helen As New Person(6, "Helen")

        'Load people into groups
        Dim Group1 As New List(Of Person)
        Group1.Add(Peter)
        Group1.Add(Jeff)
        Dim Group2 As New List(Of Person)
        Group2.Add(Chantal)
        Group2.Add(Tammy)
        Dim Group3 As New List(Of Person)
        Group3.Add(Dave)
        Group3.Add(Helen)

        'Create a list of lists. Each list will be processed as a separate task
        Dim MySplitList As New List(Of List(Of Person))
        MySplitList.Add(Group1)
        MySplitList.Add(Group2)
        MySplitList.Add(Group3)

        'Create an array of 3 tasks
        Dim myTasks(2) As Task

        'Load the task array with what work it needs to do
        Dim iThreadNumber As Integer = 1
        For Each OneList As List(Of Person) In MySplitList
            Dim iNEW As Integer = iThreadNumber

            myTasks(iNEW - 1) = System.Threading.Tasks.Task.Run(Sub() ProcessOneChunkOfPeopleData(OneList, iNEW))

            iThreadNumber += 1
        Next OneList

        'Wait for all tasks to complete.
        Task.WaitAll(myTasks)

        m_Logger.Info("Run button code has completed")
        m_Logger.Info("############################")

        MessageBox.Show(Me, "Process complete", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    Private Async Sub ProcessOneChunkOfPeopleData(ByVal arPeople As List(Of Person), ByVal iSectionNumber As Integer)
        Try
            m_Logger.Info(String.Format("Thread #{0} contains {1} people", iSectionNumber, arPeople.Count))

            Dim iCounter As Integer = 1

            For Each xOnePerson As Person In arPeople
                'Simulate a call to a web service
                Dim bWorkedOkay1 As Boolean = Await FakeApiCall()

                m_Logger.Info(String.Format("Thread {0}. Finished processing person {1} ({2} of {3})", iSectionNumber, xOnePerson.Name, iCounter, arPeople.Count))

                iCounter += 1
            Next xOnePerson

            m_Logger.Info(String.Format("Thread #{0} has finished processing all people", iSectionNumber))
        Catch Exp As Exception
            m_Logger.Error(Exp.Message)
        End Try
    End Sub

    Public Async Function FakeApiCall() As Task(Of Boolean)
        Dim http As New HttpClient
        'This is the API which is causing the task WaitAll to not wait
        Dim xResponse As HttpResponseMessage = Await http.GetAsync("https://jsonplaceholder.typicode.com/posts/1")
        Dim sResult As String = Await xResponse.Content.ReadAsStringAsync()
        Return True
    End Function

End Class

Public Class Person
    Public Property Name As String = ""
    Public Property ID As Integer = 0

    Public Sub New()
        '
    End Sub

    Public Sub New(ByVal iID As Integer, ByVal sName As String)
        Me.ID = iID
        Me.Name = sName
    End Sub
End Class

1 Ответ

0 голосов
/ 06 июля 2018

Я не могу объяснить, почему код работает, когда вы заменяете вызов API на спящий. Я могу предложить это как решение, чтобы исправить код и заставить его ждать завершения WaitAll.

Ниже приведены три изменения, которые необходимо выполнить, чтобы проект заработал. Изменения сделаны, чтобы превратить Sub в функцию - поэтому теперь он вынужден что-то возвращать.

В btnRun_Click, изменить

myTasks(iNEW - 1) = System.Threading.Tasks.Task.Run(Sub() ProcessOneChunkOfPeopleData(OneList, iNEW))

будет

myTasks(iNEW - 1) = System.Threading.Tasks.Task.Run(Function() ProcessOneChunkOfPeopleData(OneList, iNEW))

Изменить функцию ProcessOneChunkOfPeopleData с этого

Private Async Sub ProcessOneChunkOfPeopleData(ByVal arPeople As List(Of Person), ByVal iSectionNumber As Integer)

к этому

Private Async Function ProcessOneChunkOfPeopleData(ByVal arPeople As List(Of Person), ByVal iSectionNumber As Integer) As Task(Of Boolean)

Добавьте это к самой последней строке ProcessOneChunkOfPeopleData

Return True
...