2011-01-26 60 views
6

这是关于锁定两个List(Of T)对象的previous question的后续操作。答案很有帮助,但给我留下了另一个问题。使用SyncLock的正确方法(一般情况下)

假设我有这样的功能:

Public Function ListWork() As Integer 
    List1.Clear() 
    ..Some other work which does not modify List1.. 
    List1.AddRange(SomeArray) 
    ..Some more work that does not involve List1.. 
    Retrun List1.Count 
End Function 

驻留在声明列表1类。在多线程环境中,我现在明白我应该有一个用于List1的私有锁定对象,并在修改或枚举时锁定List1。我的问题是,我应该这样做:

Private List1Lock As New Object 
Public Function ListWork() As Integer 
    SyncLock List1Lock 
    List1.Clear() 
    End SyncLock 
    ..Some other work which does not modify List1.. 
    SyncLock List1Lock 
    List1.AddRange(SomeArray) 
    End SyncLock 
    ..Some more work that does not involve List1.. 
    SyncLock List1Lock 
    Dim list1Count As Integer = List1.Count 
    End SyncLock 
    Retrun list1Count 
End Function 

或本:

Private List1Lock As New Object 
Public Function ListWork() As Integer 
    SyncLock List1Lock 
    List1.Clear() 
    ..Some other work which does not modify List1.. 
    List1.AddRange(SomeArray) 
    ..Some more work that does not involve List1.. 
    Dim list1Count As Integer = List1.Count 
    End SyncLock 
    Retrun list1Count 
End Function 

我猜测,前者的例子是最优的?

回答

9

从这些例子中,很难判断哪一个是正确的,如果是的话。一些指导方针/观察虽然可以帮助你回答你的问题,或知道提供什么额外的信息:

首先,你必须同步?每个线程都有一个这个类的实例会更有意义吗?如果每个实例都是线程本地的,并且只在该线程上修改和使用,则不需要锁定。

如果此类和利用线程的目的是为了并行处理更大的数据集,主线程可能更合理地以某种逻辑方式划分任务,然后等待工作线程完成。在这种情况下,请不要单独管理线程,而应查看ThreadPool并等待句柄。大部分肮脏的工作都是为你完成的。

关于通常的同步/锁定:如果您的操作在步骤之间中断,数据是否一致/有效?

在你的例子中,假设你有两个线程。第一个是在.AddRange().Count之间的区域,当第二个线程出现时进入函数,并获取列表中的锁。

线程1运行多一点,并击中守护.Count方法的锁,然后进入睡眠状态。在此期间,线程2清除列表,然后释放其锁,然后将线程1唤醒,然后获取锁。

在这种情况下,当线程1完成工作以构建列表时,线程1将从该函数返回0。然后,列表长度不会真的为0,因为线程2已经出现并填充了列表。

在这种情况下,各个列表操作周围的锁会破坏程序,所以在ClearCount调用之间围绕一个锁定更有意义。

简而言之,多线程是引入与Race Conditions相关的一整类微妙错误的好方法,这通常会导致Heisenbugs

在可以的时候避免线程通常是明智的。如果你不能,尝试以最小同步的方式来安排工作负载(例如,在开始时给线程一组数据,然后等待它发出完成信号,例如链接线程池示例)。如果你不能这样做,那就小心行事,并且总是问自己:“如果两条线在这个区域运行会发生什么?”。

希望这可以帮助武装你在多线程代码中的未来冒险。

+0

我想我现在明白这一点。非常微妙。我应该参加一个课或其他...感谢您的洞察力。 – 2011-01-26 10:09:58

6

“这取决于”。 这两个例子有不同的语义。

在后面的例子中,整套操作相对于锁是原子的。在前面的例子中,虽然访问列表被锁定,但整套操作无法(正确)视为原子(相对于锁)。

想象一下,如果在不同的线程上调用ListWork对同一个对象的操作/线程交错,将会发生什么情况。

快乐编码。

相关问题