VB.Net 中多线程方法中局部变量的行为

Behaviour Of Local Variables In A Multi-Threaded Method In VB.Net

提问人:CodeAdze 提问时间:4/23/2023 最后编辑:CodeAdze 更新时间:4/24/2023 访问量:58

问:

下面的代码摘自 MSDN 中的示例。这是关于如何使用的示例,但在我看来,其中有一个竞争条件。 为什么我认为这是因为方法中的线条。在我看来,可能会发生以下情况:SpinLockDim lockTaken As Boolean = FalseUpdateWithSpinLock

线程 1 进入方法并执行,并发生上下文切换。
线程 1 现在具有 和 为 True。
线程 2 进入并仅执行到行 。
线程 2 现在已设置回 False,并发生上下文切换。
线程 1 继续并在块中进行测试,发现它是 False(线程 1 应该是 True),因此不会释放 .
线程 1 退出该方法,使锁留在原位,线程 2 永远等待。
UpdateWithSpinLock_queue.Enqueue(d)SpinLocklockTakenDim lockTaken As Boolean = FalselockTakenlockTakenFinallySpinLock

Imports System.Threading
Imports System.Threading.Tasks

Class SpinLockDemo2

    Const N As Integer = 100000
    Shared _queue = New Queue(Of Data)()
    Shared _lock = New Object()
    Shared _spinlock = New SpinLock()

    Class Data
        Public Name As String
        Public Number As Double
    End Class

    Shared Sub Main()
        UseSpinLock()

        Console.WriteLine("Press a key")
        Console.ReadKey()

    End Sub

    Private Shared Sub UpdateWithSpinLock(ByVal d As Data, ByVal i As Integer)

        Dim lockTaken As Boolean = False
        Try
            _spinlock.Enter(lockTaken)
            _queue.Enqueue(d)
        Finally

            If lockTaken Then
                _spinlock.Exit(False)
            End If
        End Try
    End Sub

    Private Shared Sub UseSpinLock()
        Dim sw = Stopwatch.StartNew()

        Parallel.Invoke(
               Sub()
                   For i As Integer = 0 To N - 1
                       UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
                   Next
               End Sub,
                Sub()
                    For i As Integer = 0 To N - 1
                        UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
                    Next
                End Sub
            )
        sw.Stop()
        Console.WriteLine("elapsed ms with spinlock: {0}", sw.ElapsedMilliseconds)
    End Sub
End Class

我对此的解释是否正确。如果不是,你能告诉我我错过了什么。

vb.net 多线程 旋转锁

评论

0赞 nbk 4/23/2023
看起来您尝试对新的信号量进行编程,这些信号量在内核级别锁定和等待
1赞 Hans Passant 4/23/2023
假设布尔值由所有线程共享。事实并非如此,局部变量和方法参数对于每个线程都有不同的值。它们存储在堆栈中,每个线程都有自己的堆栈。共享不会改变这一点。lockTaken 的唯一作用是确保再次释放锁,即使线程被 Thread.Abort 或异常中止也是如此。
0赞 CodeAdze 4/23/2023
@HansPassant啊明白了,谢谢汉斯,我现在明白了,这很有帮助。

答:

2赞 jmcilhinney 4/23/2023 #1

在该代码中,是一个局部变量,因此在一个线程中运行的该方法的任何实例都不能更改在另一个线程上运行的方法的另一个实例中该变量的值。您所描述的内容需要一个成员变量。lockTakenlockTaken

评论

0赞 CodeAdze 4/23/2023
感谢您的回答,让我感到困惑的是该方法是共享的,因此不是每个线程都使用相同的方法“实例”来设置和重置相同的方法吗?UpdateWithSpinLocklockTaken
1赞 jmcilhinney 4/23/2023
@CodeAdze,如果该方法不是,并且您在同一对象上多次调用它,您是否希望在调用之间共享局部变量?我猜不是,那么为什么一种方法的行为应该不同呢? 仅表示类型一个,而不是每个类型的实例一个。但是,每个方法调用仍然是独立的。SharedSharedShared
0赞 CodeAdze 4/24/2023
。您是否希望在调用之间共享局部变量?我猜不是......不幸的是,这就是我的误解所在。正如 @Hanns Passant 所解释的那样,我不知道每个线程都有自己的堆栈和局部变量值,所以我认为如果 thread2 在 thread1 退出之前输入了一个方法,那么 thread2 将开始重新设置 thread1 仍在操作的局部变量值。我有一种感觉,我误解了某些东西,因为我的思维导致我一直在抓取 s,这基本上使我所谓的并行代码同步执行。SyncLock
1赞 Craig 4/24/2023
Shared表示例程不在类的单个实例的上下文中运行---与普通成员例程不同,没有 .它仍然可以访问私有和受保护的成员(给定一个实例作为参数、内部构造或以其他方式获取)。它不会改变例程自身变量的行为方式。如果您要访问任何变量,或者要访问共享实例上的任何内容,您仍然可能会遇到比赛。MeShared