我在ASP.net应用程序中运行的过程很长,我们迫切需要大幅缩短。该过程正在向大量信用卡收费。目前它以每秒约1次充电的速度运行。我们需要这更像是每秒10次。
因此我们决定使用多个同步线程是一种方法。所以我们基本上把这个庞大的订单列表进行处理,将列表分成十个列表,然后生成一个新线程来同时处理十个列表中的每一个。
此过程的另一个复杂因素是,我们需要报告此过程的进度,而不仅是在应用程序的任何会话中报告启动过程的用户会话,而且报告给任何用户。例如,如果我登录并开始此过程,我将看到一个进度条。如果在我启动流程并且它仍在运行之后,另一个用户登录到其他位置并转到同一页面,他们也会看到进度条。
我做了一些研究,并认为我可以使用Application变量来存储报告进度所需的相关信息。客户端在此页面上定期轮询服务器以查看是否有任何线程正在运行,如果有,它会将有关进程进度的各种统计信息返回给客户端。
看起来这种方法不起作用。当前运行的线程数的简单计数器不能按预期工作。似乎所谓的Application对象的线程安全是安全的,因为没有两个线程能够同时访问同一个变量,但是如果两个线程都试图递增一个变量,其中一个将是能够递增它,而另一个不会,而不是排队并依次递增它,第二个线程继续前进。我确信这是我的线索安全无知。
另一个问题是使用Debug.Print或Debug.WriteLine似乎与Application对象的“线程安全”相同。当每个线程启动时,我们使用Debug.WriteLine输出线程的名称和开始时间,并在完成时,我们做同样的事情来写它完成。我们始终看到十个线程启动,四个线程在调试窗口中结束。
我认为我们不需要使用Application.Lock()和Application.Unlock(),但我在每次写入操作之前和之后都尝试过这些调用,但无效 - 结果是两种方式都一样。
我有很多代码,所以我不确定要分享哪些部分,但这里有一些相关部分:
这是我们创建和启动线程的方式:
For Each oBatch As List(Of Guid) In oOrderBatches
Dim t As New Threading.Thread(Sub() ProcessPaymentBatch(oBatch, clubrunid, oToken.UserID))
t.IsBackground = True
t.Start()
Next
这是每个线程启动的子:
Private Sub ProcessPaymentBatch(oBatch As List(Of Guid), clubrunid As String, UserID As Guid)
ThreadsRunning(clubrunid) += 1
Try
Debug.Print("Thread Start")
For Each oID As Guid In oBatch
‘Do a bunch of processing stuff…
Next
Finally
ThreadsRunning(clubrunid) -= 1
Debug.Print("Thread End")
End Try
End Sub
最后,这是线程尝试访问的一个应用程序变量的示例,但似乎失败了。
Private Const _THREADSRUNNING As String = "ThreadsRunningThisRun_"
Public Property ThreadsRunning(clubid As String) As Integer
Get
Dim sToken As String = _THREADSRUNNING & clubid
If Application(sToken) Is Nothing Then
ThreadsRunning(clubid) = 0
End If
Return Application(sToken)
End Get
Set(ByVal value As Integer)
Debug.Print(value)
Dim sToken As String = _THREADSRUNNING & clubid
Application.Lock()
Application(sToken) = value
Application.UnLock()
End Set
End Property
此属性的Debug输出如下所示:
Thread Start
1
Thread Start
Thread Start
1
1
4
Thread End
5
3
Thread Start
6
3
1
-1
Thread End
-2
-3
我无法理解为什么会有不同数量的“Thread Start”和“Thread End”调试语句,我不明白线程计数如何得到负数。这就是为什么我对Application和Debug对象的线程安全性感到困惑。
非常感谢您对此事的帮助!
答案 0 :(得分:0)
为了澄清,问题在于我们在写入时将全局变量锁定在应用程序对象中,而不是在读取时。然后我们尝试在阅读时锁定,但仍然有同样的问题。我们没有意识到的是,当递增一个值时,您将获得当前值,添加到该值,然后设置新值。需要锁定所有这三个操作,所以它是这样的:
我们之前做的是:
允许多个线程获取然后设置相同的值,这解释了我们在调试窗口中看到的所有奇怪的事情。