我想知道以下类是否是线程安全的,并且在多线程上下文中可以完美地工作吗?
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方法,split方法中的许多线程调用AddCommand,commandList指向内存中的某个列表,分配新列表时,其所有内容都位于oldList中
拆分的目的是我想获取所有排队的命令,并在下一个调用中返回其余命令,...,同时让应用程序将新项目添加到commandList。
因为我专注于split方法,我忘记为添加操作添加锁,因为它不是线程安全的,谢谢:(可以poyrazoğlu),但问题仍然存在于Split
最佳答案
这里的问题归结为您所期望的行为。例如,采用您的split方法:
public List<int> Split()
{
List<int> oldList = commandList;
commandList = new List<int>();
return oldList;
}
在分配
oldList
和重新分配commandList
之间有一段时间,该AddCommand
方法可以向commandList
添加值,这些值将出现在oldList
中:public List<int> Split()
{
// Say commandList contains 1 and 2
List<int> oldList = commandList;
// Now, on another thread, this happens:
//
// AddCommand 3
// AddCommand 4
// AddCommand 5
//
// The list hasn't been reassigned yet, so at this point oldList and
// commandList both have 1, 2, 3, 4, and 5.
commandList = new List<int>();
// Now, commandList is empty, and oldList contains 1, 2, 3, 4, and 5,
// even though it only contained 1 and 2 when you first assigned it.
return oldList;
}
此序列说明了一个事实,即
oldList
不是仅包含分配时的值的快照,而是实际上可以在分配它和重新分配commandList
之间进行修改。此代码的一件事是,您添加的每个命令都将在
oldList
或commandList
中恰好一次。无论有多少线程正在调用AddCommand
,您都不会经历任何重复。这听起来像是您要完成的工作,所以我认为您的代码是正确的。之所以有效,是因为.NET reference assignment is atomic。在分配
commandList
的过程中,没有任何时间可以调用AddCommand
将该值添加到多个列表中,或者根本不添加该值。如果我误解了你的问题,请告诉我。