Monitor.Wait实际上并没有阻止主UI线程

时间:2016-01-24 02:36:04

标签: .net vb.net winforms

我在WinForms PropertyGrid中显示了一个对象实例。一些属性getter调用最终调用Monitor.Wait的方法。

问题是Monitor.Wait在这种情况下似乎不能可靠地阻止主UI线程,而且看起来PropertyGrid似乎会尝试加载其他属性,这些属性将重新输入代码的关键部分。我想正在发生的事情是应用程序消息循环以某种方式恢复,即使线程应该通过调用Monitor.Wait()来阻止。

我已使用此代码重现了该行为:

Public Class Form1
  Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim inst As New Test1
    Me.PropertyGrid1.SelectedObject = inst
  End Sub
End Class


Public Class Test1
  Private WithEvents pTimer As New System.Timers.Timer(1000) With {.AutoReset = False}

  Dim rand As New Random()

  Private pLock1 As New Object
  Private pLock2 As New Object

  Public Property Prop1 As Integer
    Get
      Static inst As Integer = 0
      inst += 1
      Return ReadVal("Prop1", inst)
    End Get
    Set(value As Integer)

    End Set
  End Property

  Public Property Prop2 As Integer
    Get
      Static inst As Integer = 0
      inst += 1
      Return ReadVal("Prop2", inst)
    End Get
    Set(value As Integer)

    End Set
  End Property

  Public Property Prop3 As Integer
    Get
      Static inst As Integer = 0
      inst += 1
      Return ReadVal("Prop3", inst)
    End Get
    Set(value As Integer)

    End Set
  End Property

  Private pInRead As Boolean = False

  Private Function ReadVal(ByVal propName As String, ByVal checkNumber As Integer) As Integer
    Dim rtn As Integer = 0
    Static inst As Integer = 0
    Dim myinst As Integer
    SyncLock pLock1
      inst += 1
      myinst = inst
      Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId & ": ReadVal enter pLock1 readval:{0}, propName:{1}, checkNumber{2}", myinst, propName, checkNumber)
      If pInRead Then
        Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId & ": pInread already true readval:{0}, propName:{1}, checkNumber{2}", myinst, propName, checkNumber)
      End If

      pInRead = True
      SyncLock pLock2
        Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId & ": waiting for pulse readval:{0}, propName:{1}, checkNumber{2}", myinst, propName, checkNumber)
        System.Threading.Monitor.Wait(pLock2)
        Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId & ": pulse received readval:{0}, propName:{1}, checkNumber{2}", myinst, propName, checkNumber)
        rtn = rand.Next(1, 100)
      End SyncLock
      pInRead = False
    End SyncLock
    Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId & ": ReadVal exit pLock1 readval:{0}, propName:{1}, checkNumber{2}", myinst, propName, checkNumber)
    Return rtn
  End Function

  Private Sub pTimer_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles pTimer.Elapsed
    SyncLock pLock2
      Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId & ": timer got pLock2")
      System.Threading.Thread.Sleep(1000)
      Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId & ": timer pulsing")
      System.Threading.Monitor.Pulse(pLock2)
    End SyncLock
    pTimer.Start()
  End Sub

  Public Sub New()
    pTimer.Start()
  End Sub
End Class

要生成行为,我只需要单击Button1,然后单击PropertyGrid几次。 以下是控制台输出部分,显示发生的问题:

8: ReadVal enter pLock1 readval:9, propName:Prop3, checkNumber3
8: waiting for pulse readval:9, propName:Prop3, checkNumber3
8: ReadVal enter pLock1 readval:10, propName:Prop1, checkNumber5
11: timer got pLock2
8: pInread already true readval:10, propName:Prop1, checkNumber5

所以问题,如输出中所示,是在主线程(线程ID 8)中输入ReadVal以填充PropertyGrid中的Prop3,调用Monitor.Wait,而不是完全阻塞线程直到Pulse从pTimer_Elapsed,我们看到ReadVal以某种方式从同一个线程(线程ID 8)输入AGAIN以填充PropertyGrid中的Prop1。 由于它是同一个线程,因此Synclock实际上无法防止重新进入的关键块。在这个例子中它并不重要,但在我的实际应用程序中,我正在访问锁定部分中的共享资源,并且重新进入会导致一些大问题。

为什么调用Monitor.Wait时主线程无法可靠阻塞?知道它没有,解决这个问题的最佳方法是什么?到目前为止,我的解决方案是始终强制ReadVal进入新线程,但必须有更好的方法。

0 个答案:

没有答案