我想知道以下类是否是线程安全的,并且在多线程上下文中可以完美地工作吗?

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之间进行修改。

此代码的一件事是,您添加的每个命令都将在oldListcommandList中恰好一次。无论有多少线程正在调用AddCommand,您都不会经历任何重复。这听起来像是您要完成的工作,所以我认为您的代码是正确的。

之所以有效,是因为.NET reference assignment is atomic。在分配commandList的过程中,没有任何时间可以调用AddCommand将该值添加到多个列表中,或者根本不添加该值。

如果我误解了你的问题,请告诉我。

08-04 16:06