如何在.NET Framework 4.0中限制最多20个线程发送Web请求?
.NET Framework 4.0下实现最多20线程批量发送Web请求
方案一:使用Parallel.ForEach(快速改造首选)
Parallel.ForEach是.NET 4.0自带的并行循环工具,能直接控制最大并发数,适合新手快速修改现有代码。
修改后的SendToServer函数
Friend Function SendToServer(ByVal Datos As List(Of String), ByVal User As String, ByVal Password As String) As Integer Dim ErrorCount As Integer = 0 BGW_Report("Sending " & Datos.Count.ToString & " event(s).", Color.Purple) Try ' 配置并行循环,限制最大并发数为20 Dim parallelOpts As New ParallelOptions() parallelOpts.MaxDegreeOfParallelism = 20 Parallel.ForEach(Datos, parallelOpts, Sub(Eventox) Dim data = Encoding.UTF8.GetBytes(Eventox) Dim xUri As New Uri(My.Settings.WebAddress) Dim result_post As String = "" Try result_post = SendRequest(xUri, data, "application/json", "POST") ' 原子性更新错误计数,避免多线程冲突 If Not result_post.Contains("Process OK") Then Interlocked.Increment(ErrorCount) End If ' 若BGW_Report需更新UI,必须切换到UI线程执行 Me.Invoke(Sub() BGW_Report($"Sent event: {Eventox}", Color.Green) End Sub) Catch ex As Exception Interlocked.Increment(ErrorCount) ' 线程安全地报告错误 Me.Invoke(Sub() BGW_Report($"Failed to send event: {Eventox}", Color.Red) BGW_Report(ex.Message, Color.Red) BGW_Report(ex.StackTrace, Color.OrangeRed) End Sub) End Try End Sub) Catch ex As Exception BGW_Report($"Parallel processing failed: {ex.Message}", Color.Red) BGW_Report(ex.StackTrace, Color.OrangeRed) ErrorCount = -1 End Try Return ErrorCount End Function
核心要点
- MaxDegreeOfParallelism:直接设置为20,确保同时运行的任务不超过20个。
- Interlocked.Increment:保证多线程环境下错误计数的准确性,避免并发修改导致的数值异常。
- Control.Invoke:如果BGW_Report是更新UI的方法,必须通过Invoke切换到UI线程,否则会触发跨线程操作异常。若你的BGW_Report内部已经处理了线程切换(比如基于BackgroundWorker的ReportProgress),可省略此步骤。
方案二:使用SemaphoreSlim控制并发(更灵活)
如果需要更精细地管控并发流程,比如手动控制任务启动顺序,可采用SemaphoreSlim实现信号量限流。
修改后的SendToServer函数
Friend Function SendToServer(ByVal Datos As List(Of String), ByVal User As String, ByVal Password As String) As Integer Dim ErrorCount As Integer = 0 ' 创建信号量,允许最多20个并发任务 Dim semaphore As New SemaphoreSlim(20) Dim taskList As New List(Of Task)() BGW_Report("Sending " & Datos.Count.ToString & " event(s).", Color.Purple) Try For Each Eventox As String In Datos ' 获取信号量,超过20个任务时自动阻塞 semaphore.Wait() ' 启动异步任务处理单个请求 Dim task = Task.Factory.StartNew(Sub(state) Dim eventData As String = CStr(state) Dim data = Encoding.UTF8.GetBytes(eventData) Dim xUri As New Uri(My.Settings.WebAddress) Dim result_post As String = "" Try result_post = SendRequest(xUri, data, "application/json", "POST") If Not result_post.Contains("Process OK") Then Interlocked.Increment(ErrorCount) End If Me.Invoke(Sub() BGW_Report($"Sent event: {eventData}", Color.Green) End Sub) Catch ex As Exception Interlocked.Increment(ErrorCount) Me.Invoke(Sub() BGW_Report($"Failed to send event: {eventData}", Color.Red) BGW_Report(ex.Message, Color.Red) BGW_Report(ex.StackTrace, Color.OrangeRed) End Sub) Finally ' 任务完成后释放信号量,允许新任务进入 semaphore.Release() End Try End Sub, Eventox) taskList.Add(task) Next ' 等待所有任务执行完毕 Task.WaitAll(taskList.ToArray()) Catch ex As Exception BGW_Report($"Concurrent processing failed: {ex.Message}", Color.Red) BGW_Report(ex.StackTrace, Color.OrangeRed) ErrorCount = -1 Finally ' 释放信号量资源 semaphore.Dispose() End Try Return ErrorCount End Function
核心要点
- SemaphoreSlim(20):设定同时允许执行的任务上限为20。
- Wait/Release:每个任务启动前获取信号量,完成后释放,确保并发数始终不超过设定值。Release放在Finally块中,避免任务异常导致信号量无法释放。
- Task.Factory.StartNew:.NET 4.0中创建异步任务的标准方式,比手动创建Thread更高效、易管理。
通用注意事项
- 线程安全:所有共享变量(如ErrorCount)必须用线程安全的方式更新,Interlocked类是最简便的选择。
- UI线程约束:任何修改UI的操作必须在UI线程执行,避免跨线程异常。
- 异常隔离:每个子任务内部单独捕获异常,防止单个请求失败导致整个批量任务中断。
- SendRequest兼容性:原SendRequest函数可直接复用,因为每个请求都是独立的WebRequest实例,线程安全。
内容的提问来源于stack exchange,提问作者E_Blue
相关产品推荐
相关产品推荐

