.NET Thread不安全代码

问题描述:

我明白COM中多线程公寓和单线程公寓的区别。.NET Thread不安全代码

请参阅下面的代码:

'VB.NET 
Imports Project1 
Imports System.Threading 

Public Class Form1 

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 
     Dim t1 As New Thread(AddressOf PersonTest.Test2) 
     Dim t2 As New Thread(AddressOf PersonTest.Test2) 
     Dim t3 As New Thread(AddressOf PersonTest.Test2) 
     t1.Name = "Test1" 
     t2.Name = "Test2" 
     t3.Name = "Test3" 
     t1.Start() 
     t2.Start() 
     t3.Start() 
    End Sub 

End Class 

Public Class PersonTest 
    Public Shared Sub Test2() 
     Try 
      Dim c1 As Class1 
      c1 = New Class1 
      For test3 As Integer = 0 To 10000 
       For test As Integer = 0 To 10000 
        Dim test2 As Short = c1.Add(CShort(test)) 
        If test2 <> test + 1 Then 
         MsgBox("Problem here") 
        End If 
       Next 
      Next 
      MsgBox("finished") 
     Catch ex As Exception 

     End Try 
    End Sub 
End Class 

Public Class Person 
    Public id As Integer 
End Class 

'VB6 - Project1.vbp,class1 
Public Test2 As Integer 

Public Function Add(ByVal TestParameter As Integer) As Integer 
Test2 = TestParameter + 1 
Add = Test2 
End Function 

基于我已阅读,我希望“MSGBOX(‘问题就在这里’)”出现,因为多个线程可以改变Person.ID的值了同步,但我已经测试了这个程序很多次,它从来没有发生过。我明白线程“没有任何保证”。上面的代码是否会在理论上造成问题?如果答案是否定的,那么如何修改代码来引发问题?我正在学习如何编写线程安全代码,为了做到这一点,我必须首先了解代码如何可能不安全。

+0

我假设你在多线程上调用它?我认为对MsgBox的调用必须在UI线程上(不是它影响线程问题) – Rob 2013-04-11 09:21:12

+0

@Rob,代码显示在Form.Load处理函数中创建了三个线程。 – w0051977 2013-04-11 09:25:39

+0

和VB6函数'添加'大概是inproc而不是STA? – Rob 2013-04-11 09:26:53

VB6生成标记单元线程COM组件在注册表中。一个昂贵的词是“不是线程安全的”。您创建的线程位于MTA中,因为您没有调用Thread.SetApartmentState()。

首先,您实际上并没有测试线程不安全的代码,因为您在方法中将它分配为局部变量,所以每个线程都会获取自己的对象。局部变量存储在堆栈中,每个线程都有自己的堆栈。只有当多个线程都可以读写共享变量时,线程安全性才会受到影响。您将不得不在Form_Load()方法中创建对象,并将引用存储在表单类的成员中以便共享。

COM没有意识到你实际上没有线程问题。它会自动启动一个新线程,一个是STA为COM对象提供一个安全的家。您可以在Debug + Windows + Threads调试器窗口中看到这些线程。

它自动将工作线程对Add()函数的调用编组到该STA线程。根据公寓规则的要求。这很慢,你的代码应该需要一段时间。就像一个实验一样,在每个线程开始切换到STA之前调用SetApartmentState。现在,不再需要帮助器线程并且不需要编组,您将看到代码更快完成批次

使用Class1的共享实例将是一个更好的测试。但是更新Test2变量仍然是线程安全的,因为它位于一个单元线程对象内。然而,它获得的实际值是随机的,无论线程上次更新它。

你的大部分问题似乎是多个线程没有更新一个值的oppertunity。

在每个线程中,您正在创建一个新实例Class1,该实例有其自己的实例Test2,因此您的每个线程都是唯一运行的线程。

你的代码更改为这将迫使它exibit你想要的问题,虽然我不知道这是否回答您的问题或不...

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 

    Dim pt = New PersonTest 

    Dim t1 As New Thread(AddressOf pt.Test2) 
    Dim t2 As New Thread(AddressOf pt.Test2) 
    Dim t3 As New Thread(AddressOf pt.Test2) 
    t1.Name = "Test1" 
    t2.Name = "Test2" 
    t3.Name = "Test3" 
    t1.Start() 
    t2.Start() 
    t3.Start() 


End Sub 


Public Class PersonTest 

    Private _class As New Class1 

    Public Sub Test2() 
     Try 

      For test3 As Integer = 0 To 10000 
       For test As Integer = 0 To 10000 

        Dim test2 = _class.Add(test) 
        If test2 <> test + 1 Then 
         MsgBox("Problem here") 
        End If 
       Next 
      Next 
      MsgBox("Finished") 
     Catch ex As Exception 
      MsgBox(ex.Message) 
     End Try 
    End Sub 
End Class 

Public Class Person 
    Public id As Integer 
End Class 

Public Class Class1 

    'VB6 - Project1.vbp,class1 
    Public Test2 As Integer 

    Public Function Add(ByVal TestParameter As Integer) As Integer 
     Test2 = TestParameter + 1 
     Add = Test2 
    End Function 

End Class