2014-10-16 72 views
0

我在这里做的是在多线程中通过for-each和index方法导航一个只读列表。结果看起来线程安全,但我不相信。 有没有人可以告诉下面的代码(从只读列表中读取)是否线程安全?如果是的话,为什么C#只读列表线程同步

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(); 
    } 
} 
+0

解释为什么你认为它不是线程安全的。 – 2014-10-17 00:06:36

回答

3

它被认为是线程安全的有多个线程的List<T>同一实例只读取如果没有作家。

线程安全

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

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

强调是我的。

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

+0

[ReaderWriterLock](http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlock%28v=vs.110%29.aspx)或[ReaderWriterLockSlim](http://msdn.microsoft.com/zh-CN/)。 com/en-us/library/system.threading.readerwriterlockslim%28v = vs.110%29.aspx)对于具有这种行为的对象来说是非常有用的工具。它会允许无限的读者,直到有人想写,然后阻止写,直到所有的读者完成(也阻止新的读者),让作家做的工作,然后畅通等待的读者。 – 2014-10-16 23:15:12

0

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

0

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

我给你说明了为什么你不应该假设它,除非它被证明的例子:

假设内部,该名单是节点的链表,包含具有值的字符串的每个节点。 搜索方法(字符串值)必须迭代节点以查找值是否存在。 显然这里没有线程安全问题。

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

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

通常情况下,数据结构不会在读取上发生突变,但除非有记录,否则不能依赖这些突变!

在名单使用的这种特殊情况下,在MSDN说,前面已经提到:

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

给出的是,你可以在线程安全的方式下进行多次读取但不写入,但不要假设它没有记录。

如果你只想使用List来读取,我建议你用一个仅在列表中公开只读操作的类来包装它,这将确保没有任何代码偶然地改变列表。