【问题标题】:In .NET how can I protect a section of code from being executed twice simultaneously在.NET中如何保护一段代码不被同时执行两次
【发布时间】:2019-08-06 05:17:26
【问题描述】:

我有一个 .net DLL,它调用了一个非线程安全的 c++ DLL。 .NET DLL 在 IIS 中运行。 IIS 应用程序池加载两个 DLL 的单个副本,并且当两个浏览器会话同时调用 .NET DLL 时,C++ DLL Ralph 就在它的鞋子上。在某些情况下,它会导致应用程序池失败,而在其他情况下,我会收到一个可捕获的错误。

我尝试使用 Monitor 类来解决我的问题:

    Private objSync As Object = New Object
.
.
.
.
        Monitor.Enter(objSync)
        editsErrorHandler(MycPlusPlusCall(_intsmfID, _strEditSetTag, _strEditLayoutTag, strNAACCRRecord,
                                            EE_SKIPFAIL, intErrCount, _ptrThis, _ptrEditsMessagerHandler),
                                           "MycPlusPlusCall")
        objMessageTransport.intMstErrorCount = intErrCount
        objMessageTransport.objMstMessages = _objErrorMessages
        '        Debug.Print("intErrCount=" & intErrCount)
        Monitor.Exit(objSync)

MycPlusPlusCall 是 c++ DLL 调用。

这似乎没有影响。

这是我第一次尝试调用非线程安全的 DLL。

如何强制应用程序池中的多个调用等待上一个调用完成?

编辑:

我将 Monitor 提升了一级,几乎每次都成功地让 W3WP 崩溃。当它崩溃时,我在 w3wp 上得到 AppCrash,模块是 c++ DLL。

上面引用的 .NET DLL 由“上方”的 DLL 调用,因此我将 Monitor.Enter & Exit 移至该级别。新的和改进的代码看起来像;

    Private Function processRecord(ByVal strInterRecordEditsName As String, _
                                   ByVal objConnection As OracleConnection) As Integer
        Dim strNAACCRRecord As String = buildNAACCRRecord(_objRecord)
        '           Dim strNAACCRRecord As String = _objRecord("NAACCR_RECORD")
        Dim objErrorMessageTransport As ACEEdits50.EditMessageTransport
        Dim intErrCount As Integer = 0
        Dim objErrorMessages As List(Of ACEEdits50.EditMessage) = New List(Of ACEEdits50.EditMessage)
        Dim strDebug As String = "Section 10"
        On Error GoTo processRecordError
        Monitor.Enter(_objACEEdits)
        ' Here is where the heavy lifting is done.
        objErrorMessageTransport = _objACEEdits.runEdits(strNAACCRRecord & Space(_intNAACCRRecordSize - strNAACCRRecord.Length))
        Monitor.Exit(_objACEEdits)

编辑 2:

所以我删除了较高的“Monitor.Enter/Exit”并按照下面的建议将其替换为 SyncLock,并添加了使对象“私有共享”的选项 所以代码看起来像;

Public Class ACEEdits50
    Public Sub New()

    End Sub
.
.
.
.
    Private Shared objSync As Object = New Object

.
.
.
.
    Public Function runEdits(ByVal strNAACCRRecord As String) As EditMessageTransport
.
.
.
.
        SyncLock objSync
            editsErrorHandler(Edit_RunEdits(_intsmfID, _strEditSetTag, _strEditLayoutTag, strNAACCRRecord,
                                                EE_SKIPFAIL, intErrCount, _ptrThis, _ptrEditsMessagerHandler),
                                               "Edit_RunEdits")
            objMessageTransport.intMstErrorCount = intErrCount
            objMessageTransport.objMstMessages = _objErrorMessages
            '        Debug.Print("intErrCount=" & intErrCount)
        End SyncLock

我仍然遇到同样的 appCrash。

【问题讨论】:

  • 如何使用互斥锁?
  • vb.net 有SyncLock
  • 你确定这不起作用吗?除了将代码放在 try/finally 块中(以便在所有情况下都调用 Monitor.Exit),这是标准模式。正如@DanielA.White 指出的那样,语言提供的SyncLock 关键字(或C# 中的lock)将为您执行此操作。
  • 你也应该使用相同的引用来锁定
  • objSync 是否需要标记为静态?很难判断这是否在实例类中,但如果是,每个实例都可能有一个 objSync。

标签: .net vb.net


【解决方案1】:

您可以使用一个命名的 Mutex,用于在同一操作系统中的进程之间进行同步

Public Class ACEEdits50

    Public Function runEdits(ByVal strNAACCRRecord As String) As EditMessageTransport
        Using m = New Threading.Mutex(False, "ACEEdits50")
            While Not m.WaitOne(20000) ' wait 20 seconds for mutex
                Console.WriteLine("Waiting for mutex")
            End While
            editsErrorHandler(Edit_RunEdits(_intsmfID, _strEditSetTag, _strEditLayoutTag, strNAACCRRecord, EE_SKIPFAIL, intErrCount, _ptrThis, _ptrEditsMessagerHandler), "Edit_RunEdits")
            objMessageTransport.intMstErrorCount = intErrCount
            objMessageTransport.objMstMessages = _objErrorMessages
            m.ReleaseMutex()
        End Using

    End Function

End Class

有关 C# 中的更多参考,请参阅 this answer

【讨论】:

  • 我确实使用了互斥锁,不完全是你的代码,但它似乎工作。其他一些解决方案也可能也有效。我这么说的原因是有人告诉我一些对 DLL 的调用是线程安全的,而另一些则不是。我只是保护那些我被告知有问题的人。我最终将所有对 DLL 的调用封装在一个互斥体中并且它起作用了。
【解决方案2】:

试试这个方法

Private Shared protectIt As New Threading.AutoResetEvent(True)
Private Sub TestSimul()
    protectIt.WaitOne() 'block forever
    '
    'code being protected
    ' when done
    protectIt.Set()
End Sub

【讨论】:

  • 我尝试了@dbasnett 定义的 Threading.AutoResetEvent(True),没有运气。结果相同。
猜你喜欢
  • 1970-01-01
  • 2021-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-16
  • 1970-01-01
  • 2010-11-25
  • 1970-01-01
相关资源
最近更新 更多