3

私は実際にマルチスレッド アプリケーションを作成したことはありません。

数回書いたことがありますが、スレッドセーフはすぐにぎこちなくなります。

インターネットには、スレッドセーフの一般的な手法に関するチュートリアルがたくさんありますが、実際のプログラミングの問題についてはあまり見つかりませんでした。

たとえば、この単純なコードを使用すると、(何も使用できません)

    StringBuilder sb = new StringBuilder();
    void Something()
    {
        sb.AppendLine("Numbers!");
        int oldLength = sb.Length;
        int i = 0;
        while (sb.Length < 500 + oldLength)
        {
            sb.Append((++i).ToString());
            //something slow
            Thread.Sleep(1000);
            if (i % 2 == 0)
            {
                sb.Append("!");
            }
            sb.AppendLine();
        }
    }

ここで、このメソッドを複数のスレッドから実行し、すべて同じ文字列ビルダーに書き込みたいとします。

一緒に同じターゲットに書き込みたいので、あるスレッドから 1 行があり、その直後に別のスレッドから別の行があり、最初のスレッドから次の行が返される可能性があります。

会話のために、あるスレッドの行が別の行の途中にあっても問題ないと仮定しましょう

コードは次のとおりです。

    StringBuilder sb = new StringBuilder();
    object sbLocker = new object();
    void SomethingSafe()
    {
        int oldLength;
        int length;

        lock (sbLocker)
        {
            sb.AppendLine("Numbers!");
            oldLength = sb.Length;
            length = oldLength;
        }


        int i = 0;

        while (length < 500 + oldLength)
        {
            lock (sbLocker)
                sb.Append((++i).ToString());

            //something slow
            Thread.Sleep(1000);
            if (i % 2 == 0)
            {
                lock (sbLocker)
                    sb.Append("(EVEN)");
            }

            lock (sbLocker)
            {
                sb.AppendLine();
                length = sb.Length;
            }
        }
    }

疲れ果てて読めない…

sb へのアクセスがあるたびに単に sbLocker をロックするようにコンパイラに指示する方法はありませんか?

このような単純なルールに対して、なぜ私のコードがそれほどぎこちなくなければならないのでしょうか? この特定の、しかし非常に使いやすいテクニックについては、あまり考えることはありません。とにかく簡単にできますか?

StringBuilder は封印されているため、継承することさえできません。

もちろん、先に進んでクラス全体をラップすることもできます:

public class SafeStringBuilder
{
    private StringBuilder sb = new StringBuilder();
    object locker = new object();
    public void Append(string s)
    {
        lock (locker)
        {
            sb.Append(s);
        }
    }

    //................
}

しかし、これはクレイジーです...私たちが使用しているさまざまなクラスが非常に多いためです。

この意味でスレッドセーフな練習をする方法はありますか?

まったく同じ結果を作成するために、より簡単な解決策があるかもしれないことは知っています...しかし、これは単なる例です。読み取り可能な解決策がなくても、同様の問題に遭遇したと確信しています。

4

2 に答える 2

1

一部のフレームワーク クラスは、使用できるスレッド セーフなラッパーを提供します。たとえばStringWriter、StringBuilder に対して を作成し、TextWriter.Synchronized を使用して、複数のスレッドから同時にアクセスできるスレッド セーフなラッパーを取得できます。

var sb = new StringBuilder();
var tw = new StringWriter(sb);

var threadSafeWriter = TextWriter.Synchronized(tw);

threadSafeWriter.Write("Hello");
threadSafeWriter.WriteLine(" world");

また、便利なスレッドセーフな並行コレクションもあります。

タイプごとにカスタムラッパーを作成せずに、「コンパイラーがアクセスされるたびにオブジェクトをロックする」ことが本当に必要な場合は、Castle.Proxy などのライブラリを使用して、実行時にラッパーを生成できます。ただし、重要なシナリオでは、複数のオブジェクト アクセスをアトミックに実行する必要がある場合、必要な結果が得られません。

于 2013-04-02T19:55:40.210 に答える