简体   繁体   中英

Thread safe StreamWriter C# how to do it? 1

What is the best way to build a program that is thread safe in terms that it needs to write double values to a file. If the function that saves the values via streamwriter is being called by multiple threads? Whats the best way of doing it?


Code from comment:

    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);
    } 

TextWriter.Synchronized

Creates a thread-safe wrapper around the specified TextWriter. ll writes to the returned wrapper will be thread safe.

Edit, after decoding the code in your comment

Your problem is not with locking or threads but with capturing the loop variable. This is a classic problem, all your threads run with a 'capture' of the single V variable, and by the time the threads are executed it will have reached its final value of 999. Except maybe for the first few Workitems.

So, instead of :

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

use

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

or, a little more concise,

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

As a variation on @Matti's answer, it is recommended to lock on a separate simple object. This reduces the risk of anything else locking on the same object (the StreamWriter code itself for instance).

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

lock (writerLock)
{
    writer.Write(...);
}

But the way you have set up the SaveValues(StreamWriter writer, ...) method make it a little more complicated. It is better to have an object where writer and writerLock are both private members.

Lock it while accessing it.

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

That gets rid of the problem of multiple threads trying to access it once. Of course, if you need to further ensure that things get written in the right order from multiple threads you'll have to add extra logic.

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

You can synchronize with a lock on the StreamWriter (or a synchronization object that is used when threads want access to the StreamWriter). However, when multiple threads are trying to write, only one will be able to continue.

If this is a problem, you can instead implement a queue, and add a writer thread. The writer thread will dequeue, and write to the StreamWriter. The other threads will enqueue items instead of writing items directly.

You will have to have some form of synchronization for access to this queue. You could simply lock it when each thread accesses it. If you aren't careful, though, could create worse performance problems than you had to begin with.

Edit

It looks like Stephen Cleary has a link to an existing .NET class you can use to implement the queue. I'll leave my answer since it explains why you'd want to do producer/consumer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM