这个算法线程安全吗?
我想知道下面的类是否是线程安全的并且在多线程上下文中完美工作?
public class ThreadSafeClass
{
private List<int> commandList = new List<int>();
public void AddCommand(int newCommand)
{
lock(syncObj) {
commandList.Add(newCommand);
}
}
public List<int> Split()
{
List<int> oldList = commandList;
commandList = new List<int>();
return oldList;
}
}
ThreadA定期调用split方法,很多线程调用AddCommand,split方法中的commandList指向内存中的某个列表,当分配新列表时,其所有内容都在oldList中
split的目的是我想获取所有排队的命令并在下一次调用中返回其余命令,...同时让应用程序向 commandList 添加新项目。
因为我专注于 split 方法,所以我忘记为添加操作添加锁,因为它不是线程安全的,感谢:(can poyrazoğlu),但 Split 的问题仍然存在
I want to know if the following class is thread safe and works perfectly in multithread context?
public class ThreadSafeClass
{
private List<int> commandList = new List<int>();
public void AddCommand(int newCommand)
{
lock(syncObj) {
commandList.Add(newCommand);
}
}
public List<int> Split()
{
List<int> oldList = commandList;
commandList = new List<int>();
return oldList;
}
}
ThreadA periodically call split method, many thread call AddCommand, commandList in the split method is pointing to some list in memory and when the new list is assigned all of its content is in oldList
The purpose of split is that I want to get all of the queued commands and in the next call return rest of them, ... and meanwhile let the application add new items to commandList.
cause I focused on split method I forgot to add lock for add operation cause it is not thread-safe THANKS TO: (can poyrazoğlu) but the question remains for Split
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
该代码不是线程安全的,因为不能保证
List.Add
是线程安全的。 (只是第一件事 - 还有其他问题)。您也没有锁定共享数据(对
commandList
的任何访问)。The code is not thread safe, as
List.Add
isn't guaranteed to be. (just the first thing - there are other problems).You are not locking on shared data (any access to
commandList
) either.这里的问题归结为您期望的行为是什么。例如,以 split 方法为例:
在分配
oldList
和重新分配commandList
之间有一段时间,AddCommand
方法可以将值添加到 < code>commandList 将出现在oldList
中:此序列表明
oldList
不是仅包含分配时的值的快照,而是实际上可以在分配时间之间进行修改并且重新分配commandList 的时间。此代码的一个事实是,您添加的每个命令将在
oldList
或commandList
中恰好出现一次。无论有多少线程调用AddCommand
,您都不会遇到任何重复。这听起来像是您想要完成的任务,所以我认为您的代码按原样是正确的。这是有效的,因为 .NET 引用分配是原子的< /a>.在分配
commandList
期间,调用AddCommand
不会导致该值添加到多个列表或根本不添加。如果我误解了您的问题,请告诉我。
The issue here boils down to what you expect the behavior to be. For example, take your split method:
There is a period of time between assigning
oldList
and reassigningcommandList
that theAddCommand
method could add values tocommandList
that will appear inoldList
:This sequence demonstrates the fact that
oldList
is not a snapshot containing only the values at the time of its assignment, but can actually be modified between the time it is assigned and the timecommandList
is reassigned.One thing that is true of this code is that each command you add will be in either
oldList
orcommandList
exactly once. You won't experience any repeats no matter how many threads are callingAddCommand
. This sounds like what you are trying to accomplish, so I think your code is correct as-is.This works because .NET reference assignment is atomic. There is no period of time during the assignment of
commandList
that a call toAddCommand
could cause the value to be added to more than one list or not be added at all.Please let me know if I misinterpreted your question.
拥有线程安全类基本上并不意味着您的程序是线程安全的。线程安全类只是意味着您可以从多个线程使用它,而且仍然没问题。线程安全基本上意味着“在多线程环境上保持一致”。
List类不是线程安全的,所以你的代码绝对不是线程安全的。但想法是,即使您使用线程安全集合,也不意味着您的代码是线程安全的。
Having thread-safe class doesn't basically mean your program is thread-safe. Thread safe class just means that you can work with it from multiple threads and it still will be fine. Thread-safety basically means "being consistent on the multithreaded environment".
The List class is not thread safe, so your code is absolutely not thread safe. But the idea is, even if you use thread-safe collection, it doesn't mean your code is thread-safe.
不。
考虑一下这样的情况:一个线程调用
AddCommand
,而另一个线程调用Split
。您的新命令可能会被清除。您需要使用锁定机制来同步它。No.
Think about the case where a thread calls
AddCommand
while another thread is callingSplit
. Your new command could potentially be cleared. You need to synchronize it by using a locking mechansim.我不认为这是线程安全的。你修改共享数据,但我看不到任何守卫。
I don't think this is thread-safe. You modify shared data, and there's no guard that I can see.
不会。
如果一个线程开始“add”操作,而另一个线程正在执行
split
操作,那么就会出现并发问题(提示,尽管add
的代码只有一行这是计算机本身的大量操作)No.
If one thread starts the operation of "add" while the other is doing the
split
then there's a concurrency issue (hint, although the code foradd
is only one line it is numerous operations by the computer itself)不,代码不是线程安全的。默认情况下,类不是线程安全的。
这是一个链接 为什么 List不是线程安全的?
No the code is not thread safe.By default the classes are not thread safe.
Here is a link Why is List<T> not thread-safe?
List
类无论如何都不是线程安全的。使用SynchronizedCollection
其中 Add、Remove 等方法是线程安全的(但枚举仍然不是,所以不要使用 foreach)List<T>
class is not thread-safe whatsoever. UseSynchronizedCollection<T>
where methods like Add, Remove etc. are thread-safe (enumeration still isn't though, so don't use foreach)您应该通过锁定一个对象来同步对列表的访问(在这种情况下,它应该是所有方法的同一对象),其中使用 commandList 本身是安全的,因为它不会重新创建或任何东西。
You should sync the access to the list, by locking on an object (and it should be the same object for all methods in this case), where it's safe to use the commandList itself as it is not recreated or anything.
这里的类的形状似乎揭示了解决经典生产者-消费者问题的变体的愿望:
http://en.wikipedia.org/wiki/Producer-consumer_problem
我可能会误解你的愿望。首先,也是最重要的,正如许多其他人所提到的,您的代码中没有锁定机制,因此,这两种方法之间存在固有的竞争条件。如果您不能立即理解术语“竞争条件”的含义,那么您不应该编写多线程代码,请去看看书。我不想在这里表现得粗鲁,但我分享Alex 的观点。
问题实际上变成了:您想在多线程场景中使用此类做什么?这个问题的答案将指导您为您的应用程序找到合适的同步机制。
对于以下两个示例,我们假设一个简单的互斥锁,就像其他人已经发布的那样。
如果有 100 个线程同时尝试调用
AddCommand(...)
方法怎么办?其中 99 个将被阻塞,特别是在基础列表很大且处于重新分配过程中的情况下。可以吗?这就是你想要的吗?在您正在处理的应用程序中这是不可能的吗?如果有 100 个线程,其中只有一个会调用
Split()
,而另外 99 个线程会调用AddCommand(...)
,其中一个调用Split()
将获取 99 个命令列表的子集,而不是全部,因为简单的锁定结构不会调用Split()
阻止直到全部挂起的AddCommand(...)
调用已完成,这(从数据处理的角度来看)更为理想。现在,更复杂的锁定机制可能会解决这些问题;但是,这些问题可能不存在于您的应用程序中,因此不需要解决,简单的锁定机制就足够了。
希望这可以帮助您找到正确的方向。
The shape of your class here seems to reveal a desire to solve a variant of the classic producer-consumer problem:
http://en.wikipedia.org/wiki/Producer-consumer_problem
I could be misunderstanding your desires. First, and foremost, as many other people have mentioned, there are no locking mechanisms in your code, and, therefore, there is an inherent race condition between the two methods. If you don't immediately understand what the term "race condition" means, you shouldn't be writing multithreaded code, please go hit the books. Not trying to be rude here, but I share Alex's sentiments.
The question really becomes: what do you want to do in a multithreaded scenario with this class? The answer to this question will guide you towards an appropriate synchronization mechanism for your application.
For the following two examples, let's assume a simple mutex lock, like other people have posted already.
What if there are 100 threads all trying to call the
AddCommand(...)
method at the same time? 99 of them will block, especially in a case where the underlying list is large, and is in the middle of a reallocation. Is that okay? Is that what you want? Is that impossible in the application you're working on?In the case of 100 threads, where only one of them would be calling
Split()
, and the other 99 callingAddCommand(...)
, the one callingSplit()
would get a subset of the list of 99 commands, instead of all of them, since a simple locking structure wouldn't make the call toSplit()
block until all the pendingAddCommand(...)
calls are completed, which would (from a data processing perspective) be more ideal.Now, a more sophisticated locking mechanism may solve these problems; however, these problems may not exist in your application, and therefore, don't need to be solved, and a simple locking mechanism will suffice.
Hopefully this helps you get pointed in the right direction.