我正在运营一个从各种商店导入产品Feed的网站。 这些Feed可能非常庞大,有些则高达1GB。 目前我通过循环调用导入函数来导入它们:
For i As Integer = 0 To dtAllFeeds.Rows.Count - 1
iImported = ImportFeed(dtAllFeeds(i).id)
totalProductsImported += iImported
lblStatus.Text += "FeedId: " + dtAllFeeds(i).id.ToString + "Items Imported: " + iImported.ToString
If iImported = 0 Then
MailFunctions.NotifyAdmin("feed error: dtAllFeeds(i).id.ToString)
End If
Next i
lblStatus.Text += "Total imported: " + totalProductsImported.ToString
这样可行,但随着Feed的大小或数量的增加,处理它们的时间也会增加。 所以我没有那么优雅地增加了超时:
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
_timeOut = Server.ScriptTimeout
Server.ScriptTimeout = 36000 '10 hours
End Sub
现在,我想在开始下一个任务之前不必等待每个任务完成这些任务,所以我尝试了here所述的设置,首先使用测试函数TestMultiThread
:< / p>
Protected Function TestMultiThread(ByVal Id As Integer, ByVal s As String) As Integer
LogError("s = " + s)
For i As Integer = 0 To (Id * 10000)
Next i
LogError(Id.ToString + " completed")
Return Id * 10000
End Function
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim numThreads = 20
Dim toProcess = numThreads
Dim resetEvent = New ManualResetEvent(False)
Dim i As Integer
For i = 1 To numThreads
ThreadPool.QueueUserWorkItem(New WaitCallback(Sub(state As Object)
TestMultiThread(i, toProcess.ToString)
If Interlocked.Decrement(toProcess) = 0 Then
resetEvent.[Set]()
End If
End Sub), Nothing)
Next i
resetEvent.WaitOne()
End Sub
然后我记录了这些错误:
s = 20
s = 20
s = 20
21 completed
21 completed
21 completed
s = 19
s = 18
s = 17
21 completed
21 completed
s = 16
21 completed
s = 15
21 completed
s = 14
21 completed
s = 13
21 completed
s = 12
21 completed
s = 11
21 completed
s = 10
21 completed
s = 9
21 completed
s = 8
21 completed
s = 7
21 completed
s = 6
21 completed
s = 5
21 completed
s = 4
21 completed
s = 3
21 completed
21 completed
我不理解这种记录顺序,但是我没有看到s = <value>
20个唯一值(但s=20
甚至连续3次开始并且缺失s=2
和s=1
?为什么i
在函数TestMultiThread
中始终为21?
答案 0 :(得分:2)
你有几个问题。首先,我甚至无法在For i = 0 To (i * 10000)
中使用TestMultiThread
进行编译,因为您还使用i
作为参数名称。
其次,您所看到的奇怪之处在于您将循环迭代器i
传递给TestMultiThread
,这是一个修改后的闭包 - 您自己捕获变量而不是它的价值。每个线程池委托运行时,i
的值为21,在循环体的每次迭代后递增。要解决此问题,请将i
复制到循环体内的局部变量,然后将该局部变量传递给TestMultiThread
。
最后,由于这是在ASP.NET的上下文中完成的,请注意,启动一堆新线程将抢夺它可用于处理传入请求的ASP.NET线程池线程。 Stephen Cleary explains:
在您的案例中,
- 请求开始在ASP.NET线程上处理。
Task.Run
在线程池上启动任务以进行计算。 ASP.NET线程
池必须处理(意外地)丢失其中一个线程 这个请求的持续时间。- 返回原始请求线程 到ASP.NET线程池。
- 计算完成后,即
线程完成请求并返回到ASP.NET线程
池。 ASP.NET线程池必须处理(意外)获取 另一个线程。
ThreadPool.QueueUserWorkItem
与他的示例中的Task.Run
类似 - 它会创建一个后台线程。如果您想在ASP.NET中执行“即发即弃”,请使用HostingEnvironment.QueueBackgroundWorkItem
,as Cleary suggests。如果您在导入后确实需要对其执行任何操作,那么请考虑利用asynchronous programming启动每个导入,然后同时等待它们(我假设您正在调用API - 自然异步操作 - - 因为你“从各种商店”导入订阅源。)