8

这是上一个关于锁定两个 List(Of T) 对象的问题的后续。那里的答案很有帮助,但给我留下了另一个问题。

假设我有这样的功能:

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..
  Return List1.Count
End Function

它驻留在声明 List1 的类中。在多线程环境中,我现在明白我应该为 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
  Return 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
  Return list1Count
End Function

我猜前一个例子是最优的?

4

2 回答 2

11

从这些例子中,很难判断哪个是正确的,如果有的话。一些指导/观察可能会帮助您回答您的问题,或者知道要提供哪些额外信息:

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

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

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

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

线程 1 运行多一点,并命中保护方法的锁.Count,然后进入睡眠状态。同时线程 2 清除列表,然后释放它的锁,唤醒线程 1,然后获取锁。

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

在这种情况下,单个列表操作周围的锁会破坏程序,因此Clear在调用和Count调用之间有一个锁更有意义。

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

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

希望这可以帮助您在多线程代码中进行未来的冒险。

于 2011-01-26T06:43:43.660 回答
7

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

在后一个示例中,整个操作集相对于 lock 是原子的。虽然在前一个示例中,对列表的访问受到锁的保护,但整个操作集不能(正确地)被视为原子(相对于锁)。

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

快乐编码。

于 2011-01-26T06:30:48.497 回答