vb.net 使用 SyncLock 的正确方法(一般)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4801618/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Proper way to use SyncLock (in general)
提问by Brian Mulcahy
This is a follow-up to a previous questionregarding locking on two List(Of T) objects. The answer there was helpful but left me with another question.
这是上一个关于锁定两个 List(Of T) 对象的问题的后续。那里的答案很有帮助,但给我留下了另一个问题。
Suppose I have a function like this:
假设我有一个这样的函数:
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
which resides in a class that declares List1. In a multithreaded environment, I now understand that I should have a private locking object for List1 and lock List1 whenever it's modified or enumerated. My question is, should I do this:
它驻留在声明 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
or this:
或这个:
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
I'm guessing that the former example is optimal?
我猜前一个例子是最佳的?
回答by Matt Sieker
From the examples, it's hard to tell which one is correct, if either is. A few guidelines/observations though that might help you answer your question, or know what to provide for additional information:
从这些例子中,很难判断哪一个是正确的,如果有一个是正确的。一些指南/观察可能会帮助您回答您的问题,或者知道要提供哪些附加信息:
First things first, do you have to synchronize? Would it make more sense for each thread to have an instance of this class? If each instance is local to the thread, and is only modified and used on that thread, you don't need locking.
首先,您必须同步吗?每个线程都有一个此类的实例是否更有意义?如果每个实例都是线程本地的,并且只在该线程上修改和使用,则不需要锁定。
If the purpose of this class and utilizing threads is for parallel processing of a larger data set, it might make more sense for the main thread to divide up the task in some logical way, and then wait for the worker threads to complete. In this case, instead of managing threads on your own, look into the ThreadPooland wait handles. Most of the dirty work is done for you then.
如果这个类和利用线程的目的是并行处理更大的数据集,那么主线程以某种逻辑方式划分任务,然后等待工作线程完成可能更有意义。在这种情况下,不要自己管理线程,而是查看ThreadPool和等待句柄。大多数肮脏的工作都是为你完成的。
About synchronization/locking in general: If your operation was interrupted between steps, would the data be consistent/valid?
一般关于同步/锁定:如果您的操作在步骤之间中断,数据是否一致/有效?
In your example, say you have two threads. The first is in the area between .AddRange()
and .Count
, when the second thread comes along enters the function, and acquires the lock on the list.
在您的示例中,假设您有两个线程。首先是在之间的区域.AddRange()
和.Count
,当第二个线程来一起进入功能,并获得名单上的锁。
Thread 1 runs a bit more, and hits the lock guarding the .Count
method, and goes to sleep. Thread 2 in the meantime clears the list and then releases its lock, waking Thread 1 up, which then acquires the lock.
线程 1 多运行一点,并击中保护.Count
方法的锁,然后进入睡眠状态。与此同时,线程 2 清除列表,然后释放它的锁,唤醒线程 1,然后线程 1 获取锁。
In this case, Thread 1 will have 0 returned from this function, when work was done by thread 1 to build the list. And then, the list length won't really be 0, since thread 2 has come along and filled the list.
在这种情况下,当线程 1 完成工作以构建列表时,线程 1 将从该函数返回 0。然后,列表长度不会真正为 0,因为线程 2 已经出现并填充了列表。
In this case, the locks around the individual list operations break the program, so it makes more sense to have one lock surrounding between Clear
and the Count
call.
在这种情况下,单个列表操作周围的锁会破坏程序,因此Clear
在Count
调用和调用之间设置一个锁更有意义。
In short, multi-threading is a good way to introduce a whole class of subtle bugs relating to Race Conditions, that often result in Heisenbugs.
简而言之,多线程是引入一整类与竞争条件相关的微妙错误的好方法,这些错误通常会导致Heisenbugs。
It is often wise to avoid threads when you can. If you can't, try to arrange your workload in ways that require minimal synchronization (e.g. giving the thread a set of data at the start, then waiting for it to signal completion, such as with the thread pool example linked). If you can't do that, then tread carefully, and always ask yourself "What will happen if two threads run in this area".
尽可能避免线程通常是明智的。如果不能,请尝试以需要最少同步的方式安排工作负载(例如,在开始时为线程提供一组数据,然后等待它发出完成信号,例如链接的线程池示例)。如果你不能做到这一点,那就小心行事,并始终问自己“如果两个线程在这个区域运行会发生什么”。
Hopefully this helps arm you for future adventures in multi-threaded code.
希望这有助于武装您在多线程代码中的未来冒险。
回答by Brian Mulcahy
"It depends". The two examples have different semantics.
“这取决于”。这两个示例具有不同的语义。
In the latter example the entire set of operations is atomic with respect to the lock. While in the former example access to the list is guarded in lock, but the entire set of operations can't be (correctly) viewed as atomic (with respect to the lock).
在后一个示例中,整个操作集相对于锁而言是原子的。虽然在前一个示例中,对列表的访问受锁保护,但不能(正确地)将整个操作集视为原子(相对于锁)。
Imagine what would/could happen in terms if operation/thread interleaving if ListWork
was invoked upon the same object by different threads.
想象一下,如果不同线程ListWork
对同一对象调用操作/线程交错,将会/可能会发生什么。
Happy coding.
快乐编码。