Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
ASP.net线程安全混乱_Asp.net_Multithreading - Fatal编程技术网

ASP.net线程安全混乱

ASP.net线程安全混乱,asp.net,multithreading,Asp.net,Multithreading,我在ASP.net应用程序中有一个非常长的运行过程,我们迫切需要大大缩短这个过程。所讨论的过程是向大量信用卡收费。目前,它的性能大约为每秒1次充电。我们需要更接近每秒10次 所以我们决定利用多个并发线程将是一种方法。因此,我们基本上要处理这个庞大的订单列表,将列表分成十个列表,然后生成一个新线程来同时处理这十个列表中的每一个 此过程的另一个复杂之处是,我们需要报告此过程的进度,不仅要向启动此过程的用户会话报告进度,还要向应用程序中任何会话中的任何用户报告进度。例如,如果我登录并启动此过程,我将看

我在ASP.net应用程序中有一个非常长的运行过程,我们迫切需要大大缩短这个过程。所讨论的过程是向大量信用卡收费。目前,它的性能大约为每秒1次充电。我们需要更接近每秒10次

所以我们决定利用多个并发线程将是一种方法。因此,我们基本上要处理这个庞大的订单列表,将列表分成十个列表,然后生成一个新线程来同时处理这十个列表中的每一个

此过程的另一个复杂之处是,我们需要报告此过程的进度,不仅要向启动此过程的用户会话报告进度,还要向应用程序中任何会话中的任何用户报告进度。例如,如果我登录并启动此过程,我将看到一个进度条。如果在我启动进程后,它仍在运行,另一个用户登录到其他地方并转到同一页面,他们也会看到进度条

我做了一些研究,认为我可以使用应用程序变量来存储报告进度所需的相关信息。无论何时在该页面上,客户机都会定期轮询服务器,查看是否有线程在运行,如果有,它会将进程进度的各种统计信息返回给客户机

这种方法似乎不起作用。当前运行线程数的简单计数器无法按预期工作。应用程序对象的所谓线程安全性似乎是安全的,因为没有两个线程能够同时访问同一个变量,但不安全的是,如果两个线程都尝试递增一个变量,其中一个线程将能够递增该变量,而另一个线程将不能递增该变量,而不是依次排队递增该变量,第二条线继续前进。我敢肯定这是我的线程安全无知

另一个问题是,使用Debug.Print或Debug.WriteLine似乎与应用程序对象具有相同的“线程安全性”。当每个线程启动时,我们使用Debug.WriteLine输出线程的名称和启动时间,当它完成时,我们执行与它完成时相同的写入操作。在调试窗口中,我们始终看到10个线程开始,4个线程结束

我认为我们不需要使用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
此属性的调试输出如下所示:

Thread Start
1
Thread Start
Thread Start
1
1
4
Thread End
5
3
Thread Start
6
3
1
-1
Thread End
-2
-3
我不明白为什么会有不同数量的“threadstart”和“threadend”调试语句,我也不明白线程计数怎么会变成负数。这就是为什么我对应用程序和调试对象的线程安全性感到困惑的原因


非常感谢您在这件事上的帮助

没关系,我只是个白痴。这个问题与应用程序或调试对象不是线程安全的无关,问题出在我的方法中(正如预期的那样)

要澄清的是,问题是我们在编写时锁定了应用程序对象中的全局变量,而不是在读取时。然后我们也尝试在阅读时锁定,但仍然有相同的问题。我们没有意识到的是,当递增一个值时,您将获得当前值,然后将其相加,然后设置新值。锁需要桥接所有这三个操作,所以它是这样的:

Thread Start
1
Thread Start
Thread Start
1
1
4
Thread End
5
3
Thread Start
6
3
1
-1
Thread End
-2
-3
  • 得到
  • 设置
  • 解锁
我们之前所做的是:

  • 得到
  • 解锁
  • 设置
  • 解锁

这允许多个线程获取并设置彼此相同的值,这解释了我们在调试窗口中看到的所有奇怪现象。

我猜您正在更改数据。因此,更聪明的方法可能是将操作推入数据库。