处理任务最有效的方法

Most efficient way to handle tasks

我有一个 windows 表单程序,它 运行 24/7 运行,大约每 15 分钟做一些工作,然后必须进行大约 5000 次 http 调用,我不需要等待或从调用中获得任何结果,只需点击 http API 并继续。我将 http 调用作为一个单独的子程序,而我现在所做的只是为每个调用生成一个新线程。这在高 CPU 机器上工作正常,但是当我 运行 在有限 CPU 的托管机器上时,我得到大约 1/5 的线程错误并出现 HTTP 超时。

所以我的问题是如何独立于我的主线程处理 5000 多个子调用,以便尽可能快地处理它们而不阻塞我的应用程序

这是现在的基本代码:

for i = 1 to 5000
    Dim thread As New Thread(
        Sub()
            sendtoapi(i)
        End Sub
    )
    thread.SetApartmentState(ApartmentState.STA)
    Thread.Start()
next i

Private Sub sendtoapi(i as integer)
    Dim result As String = ""
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
    Using Client As New System.Net.WebClient()
        result = Client.DownloadString("https://apiurl/addnumber?num=" & i)
    End Using
End Sub

您可以使用任务并行库限制线程数。即,Parallel.For。这将很好地替代您的 For 循环。

Parallel.For(1, 5001, Sub(i) sendtoapi(i))

由于您是从表单计时器执行的 运行,因此您可以继续执行此操作并使用 Async/Await 机制进行较小的更改。

Private WithEvents Timer1 As New System.Windows.Forms.Timer _
    With {.Enabled = True, .Interval = 15 * 60 * 1000}

Private Async Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    Await Task.Run(Sub() Parallel.For(1, 5001, Sub(i) sendtoapi(i)))
End Sub

这实际上并没有创建新线程,而是使用线程池中的现有线程。好吧,在 TPL 内部肯定会创建新线程 :) 但 Async/Await 本身不会。

您完全可以使用 System.Threading.Timer 将其从 UI 中删除,而无需担心会阻塞 UI。这 运行s 在线程池线程上,TPL 将管理它是利用线程池线程还是觉得需要生成新线程。无论哪种方式,它都会让您的系统处于控制之中。

Private runTimer As New System.Threading.Timer(AddressOf run, Nothing, 0, 15 * 60 * 1000)

Sub run(state As Object)
    Parallel.For(1, 5001, Sub(i) sendtoapi(i))
End Sub