私は実際にマルチスレッド アプリケーションを作成したことはありません。
数回書いたことがありますが、スレッドセーフはすぐにぎこちなくなります。
インターネットには、スレッドセーフの一般的な手法に関するチュートリアルがたくさんありますが、実際のプログラミングの問題についてはあまり見つかりませんでした。
たとえば、この単純なコードを使用すると、(何も使用できません)
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);
}
}
//................
}
しかし、これはクレイジーです...私たちが使用しているさまざまなクラスが非常に多いためです。
この意味でスレッドセーフな練習をする方法はありますか?
まったく同じ結果を作成するために、より簡単な解決策があるかもしれないことは知っています...しかし、これは単なる例です。読み取り可能な解決策がなくても、同様の問題に遭遇したと確信しています。