【问题标题】:Thread safe StreamWriter C# how to do it? 1线程安全的StreamWriter C#怎么做呢? 1
【发布时间】:2011-04-05 04:04:09
【问题描述】:

就需要将双精度值写入文件而言,构建线程安全程序的最佳方法是什么。如果通过streamwriter保存值的函数被多个线程调用?最好的方法是什么?


评论代码:

    static void Main()
    {
        List<double> Values = new List<double>();
        StreamWriter writer = new StreamWriter("test.out");

        for (int i = 0; i < 1000; i++) Values.Add(i);

        foreach (double V in Values)
        {
            ThreadPool.QueueUserWorkItem(delegate(object state) { SaveValues(writer, V); }, V);
        }
    }

    static void SaveValues(StreamWriter writer, double Value)
    {
        lock (writer) writer.WriteLine(Value);
    } 

【问题讨论】:

    标签: c# .net multithreading streamwriter


    【解决方案1】:

    TextWriter.Synchronized

    围绕指定的 TextWriter 创建一个线程安全的包装器。 ll 写入返回的包装器将是线程安全的。

    【讨论】:

      【解决方案2】:

      编辑,解码您评论中的代码后

      您的问题不是与锁定或线程有关,而是与捕获循环变量有关。这是一个经典问题,所有线程都在“捕获”单个 V 变量的情况下运行,并且当线程执行时,它的最终值将达到 999。可能除了前几个工作项。

      所以,而不是:

      foreach (double V in Values)
      {  
         ThreadPool.QueueUserWorkItem(delegate(object state) 
              { SaveValues(writer, V); }, V); // use of captured V: almost always 999
      }
      

      使用

      foreach (double V in Values)
      {
          double W = V;
          ThreadPool.QueueUserWorkItem(delegate(object state)
             { SaveValues(writer, W); }, V);
      }
      

      或者,更简洁一点,

      foreach (double V in Values)
      {
          double W = V;
          ThreadPool.QueueUserWorkItem((state) => SaveValues(writer, W));
      }
      

      作为@Matti 答案的变体,建议锁定一个单独的简单对象。这降低了其他任何东西锁定同一对象的风险(例如 StreamWriter 代码本身)。

      private StreamWriter writer = ...;         
      private Object writerLock = new Object();  // don't expose through a property 
      // or any other way
      
      lock (writerLock)
      {
          writer.Write(...);
      }
      

      但是您设置SaveValues(StreamWriter writer, ...) 方法的方式使它有点复杂。最好有一个 writerwriterLock 都是私有成员的对象。

      【讨论】:

      • 我尝试了你的两种方法,但似乎我必须锁定其中包含 writer.write(...) 的函数。如果我按照您的建议锁定,它只会保存一个值/
      • 好的,其中一种方法实际上可以工作到 840,我想写下 1000 个值。但似乎是一种合理的方法,虽然不是 100%
      • @Wajih:这是您的 Main() 的问题。添加Console.ReadKey(); writer.Close(); 作为最后的语句。你的应用程序在线程完成之前关闭。
      • 是的,我想这是一个不错的提示
      【解决方案3】:

      您可以使用 StreamWriter 上的锁(或当线程想要访问 StreamWriter 时使用的同步对象)进行同步。但是,当多个线程尝试写入时,只有一个线程能够继续。

      如果这是一个问题,您可以改为实现一个队列,并添加一个写入器线程。 writer 线程将出列,并写入 StreamWriter。其他线程会将项目排入队列,而不是直接写入项目。

      您必须进行某种形式的同步才能访问此队列。当每个线程访问它时,您可以简单地锁定它。但是,如果您不小心,可能会产生比您开始时更糟糕的性能问题。

      编辑

      看起来 Stephen Cleary 有一个指向现有 .NET 类的链接,您可以使用它来实现队列。我会留下我的答案,因为它解释了你为什么想做生产者/消费者。

      【讨论】:

      • 我猜你是对的,排队系统在这一点上是最好的。
      【解决方案4】:

      通常在进行输入或输出时,我更喜欢producer/consumer queue,只有一个消费者负责实际写入输出。

      【讨论】:

        【解决方案5】:

        访问时锁定它。

        lock (myStreamWriter) {
            myStreamWriter.Write(...);
        }
        

        这消除了多个线程尝试访问一次的问题。当然,如果您需要进一步确保从多个线程中以正确的顺序写入内容,则必须添加额外的逻辑。

        【讨论】:

        • 好的,我想我会试试的
        • OK 所以我无法确保写入,只写入了两个值,我如何同步。流程以便我得到所有写入的值?类测试 { static void Main() { List Values = new List(); StreamWriter writer = new StreamWriter("test"); for (int i = 0; i
        • @wajih 你应该用它来更新你的问题。作为评论太难读了。
        猜你喜欢
        • 2011-04-05
        • 1970-01-01
        • 2012-05-26
        • 1970-01-01
        • 2014-06-16
        • 2016-06-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多