【问题标题】:C# Readonly List thread SynchronisationC#只读列表线程同步
【发布时间】:2014-10-16 23:05:32
【问题描述】:

我在这里做的是在多个线程中通过 for-each 和 index 方法导航只读列表。结果看起来是线程安全的,但我不相信。

谁能告诉下面的代码(从只读列表中读取)是否是线程安全的?如果是,那为什么?

public class ThreadTest
{
   readonly List<string> port;

    public  ThreadTest()
    {
        port = new List<string>() { "1", "2", "3", "4", "5", "6" };            
    }

    private void Print()
    {
        foreach (var itm in port)
        {
            Thread.Sleep(50);
            Console.WriteLine(itm+"----"+Thread.CurrentThread.ManagedThreadId);
        }
    }

    private void Printi()
    {
        for(int i=0;i<5;i++)
        {
            Thread.Sleep(100);
            Console.WriteLine(port[i] + "--iiiii--" + Thread.CurrentThread.ManagedThreadId);
        }
    }

    public void StartThread()
    {
        Task[] tsks = new Task[10];
        tsks[0] = new Task(Print);
        tsks[1] = new Task(Print);
        tsks[2] = new Task(Print);
        tsks[3] = new Task(Print);
        tsks[4] = new Task(Print);
        tsks[5] = new Task(Printi);
        tsks[6] = new Task(Printi);
        tsks[7] = new Task(Printi);
        tsks[8] = new Task(Printi);
        tsks[9] = new Task(Printi);

        foreach (var tsk in tsks)
        {
            tsk.Start();
        }

        Task.WaitAll(tsks);
    }
}

class Program
{
    static void Main(string[] args)
    {

        new ThreadTest().StartThread();

        Console.ReadLine();
    }
}

【问题讨论】:

  • 解释为什么你认为它不是线程安全的。

标签: c# multithreading synchronization thread-safety


【解决方案1】:

只有在没有写入者的情况下,让多个线程从 List&lt;T&gt; 的同一实例中读取被认为是线程安全的。

线程安全

这种类型的公共静态(在 Visual Basic 中为共享)成员是线程安全的。不保证任何实例成员都是线程安全的。

对 List 执行多次读取操作是安全的,但如果在读取时修改集合,则会出现问题。为确保线程安全,请在读取或写入操作期间锁定集合。要使一个集合能够被多个线程访问以进行读写,您必须实现自己的同步。对于具有内置同步的集合,请参阅 System.Collections.Concurrent 命名空间中的类。有关固有的线程安全替代方案,请参阅 ImmutableList 类。

重点是我的。

http://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx

【讨论】:

  • ReaderWriterLockReaderWriterLockSlim 对于具有这种行为的对象来说是非常有用的工具。它将允许无限的读者,直到有人想写,然后阻止写,直到所有读者完成(也阻止新读者),让作者完成它的工作,然后解除对等待的读者的阻止。
【解决方案2】:

如果没有人更改列表,为什么它不是线程安全的?我在这里看到的唯一问题是如果(你是超人并且)你按下键的速度比线程读取列表的速度快,然后其中一些将被主线程杀死。
所以最好等他们完成后再主要完成。

【讨论】:

    【解决方案3】:

    如果它没有被证明是线程安全的,那么不要假设它是!

    我会给你一个例子来说明为什么你不应该假设它,除非它被记录:

    假设在内部,List 是一个节点的 LinkedList,每个节点都包含一个带有值的字符串。 Search(string value) 方法必须遍历节点以查找该值是否存在。 显然这里没有线程安全问题。

    但是现在,出于性能优化的原因,假设 List 保留了一个内部数组,其中写入了最后搜索的值,这对于立即检查最常搜索的值很有用。

    你看到我们在这里做了什么吗?现在读取会更改列表的内部状态,并且突变不是线程安全的。

    通常,数据结构不会对读取进行突变,但除非记录在案,否则您不能依赖它!

    在这种使用列表的特殊情况下,msdn 说,如前所述:

    对一个 List 执行多个读取操作是安全的,但是 如果在读取集合时对其进行了修改,则可能会出现问题。 为确保线程安全,请在读取或写入期间锁定集合 手术。使集合能够被多个线程访问 对于读写,您必须实现自己的同步。 对于具有内置同步的集合,请参阅 System.Collections.Concurrent 命名空间。对于本质上线程安全的 替代方法,请参见 ImmutableList 类。

    考虑到这一点,是的,您可以以线程安全的方式进行多次读取而无需写入,但除非有文档说明,否则不要假设它。

    如果您只想使用 List 进行读取,我建议您使用一个仅公开列表读取操作的类来包装它,这将确保没有代码意外更改列表。

    【讨论】:

      猜你喜欢
      • 2011-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-14
      • 2011-06-15
      相关资源
      最近更新 更多