5

就需要将双精度值写入文件而言,构建线程安全程序的最佳方法是什么。如果通过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);
    } 
4

5 回答 5

7

TextWriter.Synchronized

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

于 2010-08-28T13:35:28.620 回答
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, ...)方法的方式使其变得更加复杂。最好有一个对象,writer它们writerLock都是私有成员。

于 2010-08-28T11:37:31.733 回答
4

访问时锁定它。

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

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

于 2010-08-28T11:26:26.340 回答
3

通常在进行输入或输出时,我更喜欢只有一个消费者来实际写入输出的生产者/消费者队列。

于 2010-08-28T11:42:07.807 回答
2

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

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

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

编辑

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

于 2010-08-28T11:53:35.140 回答