线程安全StreamWriter C#怎么做? 1

时间:2010-08-28 11:23:53

标签: c# .net multithreading streamwriter

构建一个线程安全的程序的最佳方法是什么,它需要将双值写入文件。如果多个线程正在调用通过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);
    } 

5 个答案:

答案 0 :(得分:7)

TextWriter.Synchronized

  

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

答案 1 :(得分:5)

在解码评论中的代码后编辑

你的问题是锁定或线程,但捕获循环变量。这是一个经典问题,所有线程都使用单个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都是私有成员的对象。

答案 2 :(得分:3)

访问时锁定它。

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

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

答案 3 :(得分:3)

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

答案 4 :(得分:2)

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

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

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

修改

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