首页 > 解决方案 > 控制异步操作​​的数量

问题描述

我有一个程序可以下载大约 500 个 url 并将它们保存在 SQLite Db 中。在我的程序中,我将这 500 个 url 分成更小的包(每个包大约 40 个 url),然后以 5 秒的间隔一个接一个地下载包(使用计时器控制),这样整个 500 个 url 将每分钟下载一次。

这是我获取数据的方法,为每个 url 调用。它使用 httpClient,在 Form_Load 中定义:

  Private Async Function GetTseData(address As String) As Task(Of String)

    Dim response As HttpResponseMessage = Await hc.GetAsync(address)
    Dim Str = Await response.Content.ReadAsStringAsync()
    If response.IsSuccessStatusCode Then
        Return Str
    Else
        Return ""
    End If

End Function

我的问题是有时在数据库中我会看到缺陷数据。例如,在下图中一个 url 的图像中,在 10:16 和 10:25 之间的大约 10 分钟内没有下载数据,然后突然之间我看到大约 10 个同时保存的数据(在 10:27)。正如我所说,每行之间必须有 1 分钟的间隔。

(tget 是保存在数据库中的时间。t1 和 t2 是调用上述方法之前和之后的时间)。

听起来我对异步操作的理解有问题。有什么提示吗?

编辑:这是我在 Timer_tick 中的简化代码:

  Private Async Sub timerGetData_Tick(sender As Object, e As EventArgs) Handles timerGetData.Tick
    
  
    Dim strResponseTexts As New List(Of String), Codes As New List(Of Long)
    Dim t1List, t2List As New List(Of String)    ' time before and after get tse data
    
    For rr = firstIndex To lastIndex

        Dim r As DataRow = stocksDt(rr)
        Dim code  As Long
      
        Dim address2 As String = addressMainPage & code.ToString
        Dim strFull As String = ""
        Dim t1, t2 As String    ' time before and after get tse data
        t1 = Now.ToString("hh:mm:ss.ff")
        t1List.Add(t1)
        Try
            strFull = Await GetTseData(address2)
        Catch ex As Exception
            logErrorGetData += ex.ToString
        End Try
        t2 = Now.ToString("hh:mm:ss.ff")
        t2List.Add(t2)

        If strFull = String.Empty Then
            ' some logging operations
            Continue For
        End If

       
        Codes.Add(Code)
        strResponseTexts.Add(strFull)
       
    Next
  
    ' saving each pack of data in Db
    Dim r2 As Integer = FillDb(dbFile, Codes, strResponseTexts, t1List, t2List)
       
End Sub

提示:问题与服务器无关。我在 VPS 中同时尝试了相同的程序,它正确下载并保存了 1 分钟时间范围内的所有 url。也许我不知道关于 Async/Await 的一些观点。如何调试我的程序以找到问题?

Edit2:我的 FillDb 方法:

  Public Function FillDb(dbFile As String, Codes As List(Of Long), strInputs As List(Of String), Optional t1List As List(Of String) = Nothing, Optional t2list As List(Of String) = Nothing) As Integer

    Dim TableNames, SahmValuesList As New List(Of String)

    For i = 0 To Codes.Count - 1
        Dim code As Long = Codes(i)
        Dim strInput As String = strInputs(i)

        Dim TableName As String = "s" & code.ToString

        Dim strArr() As String = strInput.Split(";")
  
        Array.Copy(strArr, 0, SahmValues, 0, 26)

        SahmValues(27) = TimeOfDay.ToString("hh:mm:ss")   ' tget field

        Dim t1 As String = "" : Dim t2 As String = ""
        If t1List IsNot Nothing And i <= t1List.Count - 1 Then t1 = t1List(i)
        If t2list IsNot Nothing And i <= t2list.Count - 1 Then t2 = t2list(i)
        SahmValues(28) = t1
        SahmValues(29) = t2

        Names.Add(TableName)
        Dim SahmValuesString As String = String.Join(",", SahmValues)
        SahmValuesList.Add(SahmValuesString)


    Next

    AddtoDb_MultiRowsMultiTable(dbFile, stockTableNames.ToArray, siDb_ColumnsName, siDb_ColumnsType, SahmValuesList)

    
    Return 1
End Function


Public Sub AddtoDb_MultiRowsMultiTable(dbFile As String, tableNames As String(), columnNames As String(), columnTypes As String(), values As List(Of String))
        If Not System.IO.File.Exists(dbFile) Then SQLiteConnection.CreateFile(dbFile)

        Using conn As SQLiteConnection = New SQLiteConnection("Data Source=" & dbFile & ";PRAGMA journal_mode=WAL;")
            conn.Open()

        Using cmd As SQLiteCommand = conn.CreateCommand()

            Using transaction As SQLiteTransaction = conn.BeginTransaction()

                For k = 0 To tableNames.Length - 1
                    Dim tablename As String = tableNames(k)
                    Dim rowString As String = values(k)
                    Dim v() As String = rowString.Split(",")

                    Dim SahmColCreation As String = "(id integer primary key AUTOINCREMENT, "
                    For i = 0 To columnNames.Length - 1
                        SahmColCreation += columnNames(i) & " " & columnTypes(i) & ", "
                    Next
                    SahmColCreation = SahmColCreation.Remove(SahmColCreation.LastIndexOf(","), 2) + ");"
                    Dim comString1 As String = String.Format("CREATE TABLE IF NOT EXISTS {0} ", tablename) & SahmColCreation

                    Dim comString2 As String = ""
                    If columnNames.Contains("t") Then
                        Dim index_name As String = "idx_" & tablename
                        comString2 = String.Format("CREATE INDEX IF NOT EXISTS {0} ON {1} (t);", {index_name, tablename})
                    End If

                    Dim SahmRowString As String = "("
                    For i = 0 To columnNames.Length - 1
                        SahmRowString += columnNames(i) + ", "
                    Next
                    SahmRowString = SahmRowString.Remove(SahmRowString.LastIndexOf(","), 2) + ")"

                    Dim SahmValues As String = "VALUES ("
                    For i = 0 To v.Length - 1
                        Dim val As String = v(i).Trim("'")
                        If columnTypes(i) = "Integer" Then
                            SahmValues += val + ", "
                        Else
                            SahmValues += "'" + val + "', "
                        End If
                    Next
                    SahmValues = SahmValues.Remove(SahmValues.LastIndexOf(","), 2) + ");"
                    Dim comString3 As String = String.Format("INSERT INTO {0} ", tablename) & " " & SahmRowString & " " & SahmValues
                    Dim comStringFull = comString1 & "; " & comString2 & "; " & comString3
                    cmd.CommandText = comStringFull
                    cmd.ExecuteNonQuery()

                Next

                transaction.Commit()
            End Using

        End Using
    End Using
   
End Sub

标签: vb.netasynchronousasync-await

解决方案


推荐阅读