5

double値をファイルに書き込む必要があるという点でスレッドセーフなプログラムを構築する最良の方法は何ですか? ストリームライターを介して値を保存する関数が複数のスレッドによって呼び出されている場合は? それを行う最良の方法は何ですか?


コメントからのコード:

    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 の周りにスレッド セーフなラッパーを作成します。返されたラッパーへのすべての書き込みは、スレッドセーフになります。

于 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, ...)方法により、メソッドは少し複雑になります。writerwriterLockが両方ともプライベートメンバーであるオブジェクトを持っている方が良いです。

于 2010-08-28T11:37:31.733 に答える
4

アクセス中はロックしてください。

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

これにより、複数のスレッドが一度アクセスしようとする問題が解消されます。もちろん、複数のスレッドから正しい順序で書き込まれるようにする必要がある場合は、ロジックを追加する必要があります。

于 2010-08-28T11:26:26.340 に答える
3

通常、入力または出力を行うときは、出力の実際の書き込みを行うコンシューマーが1つだけのプロデューサー/コンシューマーキューを好みます。

于 2010-08-28T11:42:07.807 に答える
2

StreamWriter(またはスレッドがStreamWriterへのアクセスを必要とするときに使用される同期オブジェクト)のロックと同期できます。ただし、複数のスレッドが書き込もうとしている場合、続行できるのは1つだけです。

これが問題になる場合は、代わりにキューを実装して、ライタースレッドを追加できます。ライタースレッドはデキューし、StreamWriterに書き込みます。他のスレッドは、アイテムを直接書き込む代わりに、アイテムをキューに入れます。

このキューにアクセスするには、何らかの形式の同期が必要になります。各スレッドがアクセスするときに、単にロックすることができます。ただし、注意しないと、最初から必要だったよりもパフォーマンスの問題が悪化する可能性があります。

編集

Stephen Clearyには、キューの実装に使用できる既存の.NETクラスへのリンクがあるようです。なぜあなたが生産者/消費者をやりたいのかを説明しているので、私は答えを残しておきます。

于 2010-08-28T11:53:35.140 に答える