2016年10月20日木曜日

【C#】Console.WriteLineはスレッドセーフなのか?

◆前置き
ConsoleクラスのAPI仕様

標準入力、標準出力、または標準エラー ストリームをリダイレクトするには、呼び出し、 Console.SetIn, 、Console.SetOut, 、または Console.SetError メソッドをそれぞれします。 これらのストリームを使用する I/O 操作は同期され、複数のスレッドがデータの読み取りまたはストリームに書き込むことができます。 つまり、このなど、通常は非同期のメソッド TextReader.ReadLineAsync, 、オブジェクトがコンソールのストリームを表す場合、同期的に実行します。

スレッド セーフ
この型はスレッド セーフです。


なるほど・・・、分からん。いや、だいたい分かるのだが、日本語訳ミスってるし(このなどとは一体・・・?コロラドみたいで語呂良くて、気に入ったぞ)、変な読点あるし、機械翻訳なのかな?スレッドセーフとは言ってるが、どの程度大丈夫なのか?メソッドの開始から終了までなのか?単に例外が発生しないだけで文字列がごちゃ混ぜになるのか?それとも・・・

ちなみにスレッドセーフとは・・・

一方のスレッドがあるデータを操作したことで他のスレッドが利用するデータが破壊されてしまったり、同じ手続きを同時に複数のスレッドが呼び出したことで予期しない結果が生じたりしないよう、対策を講じる必要がある。


ということで、動作確認してみる。

◆サンプルコード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleThreadSafe
{
    class Program
    {
        static void Main(string[] args)
        {
            var strAry = new string[] {
                "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
                "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
                "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
                "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
                "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE",
                "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
            };
            Console.WriteLine("start.");
            foreach (var count in Enumerable.Repeat(0, 1000))
                strAry.AsParallel().ForAll(Console.WriteLine);
            Console.WriteLine("end.");
            Console.ReadKey();
        }
    }
}

◆実行結果(画面)


◆実行結果(まとめ)
・実行結果をエディタに貼り付け、start. end.の行を除くと6000行。
・また、全ての行が以下のいずれかであることが確認できた。
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

◆Console.Writeはどうか?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleThreadSafe
{
    class Program
    {
        static void Main(string[] args)
        {
            var strAry = new string[] {
                "This_is_a_pen,_but_I'm_not_a_pen.",
                "Hello_World.",
                "I'm_HogeHoge.",
                "My_name_is_Panda.",
                "C#_is_cool.",
                "I'm_fooooooooooooooooooooooool.",
            };
            Console.WriteLine("start");
            foreach (var count in Enumerable.Repeat(0, 1000))
                strAry.AsParallel().ForAll(Console.Write);
            Console.WriteLine("end");
            Console.ReadKey();
        }
    }
}


◆実行結果(画面)


◆実行結果(まとめ)
・実行結果をエディタに貼り付け、start endの行を除くと6000行。
・また、.で区切ると全ての文字列が以下のいずれかであることが確認できた。
C#_is_cool.
Hello_World.
I'm_HogeHoge.
I'm_fooooooooooooooooooooooool.
My_name_is_Panda.
This_is_a_pen,_but_I'm_not_a_pen.

※スペースを使わずに_(アンダースコア)を使っている理由
 コマンドプロンプトの右端にスペースがあると、コピー&ペーストの際にスペースが削除されるため、アンダースコアを使用している。リダイレクト使おうとも思ったが、本当に想定した検証になるか不安があったので使用しなかった。

◆課題

  • 本当にマルチスレッドになってるのか確認する。確認してないんじゃ、この記事意味ないよ?
  • Console.WriteLine(str)を実行して確認してみる。
  • Console.Write(str)を実行して確認してみる。
  • AsParrallesを辞めてみる。
  • 速度の確認をする。

0 件のコメント:

コメントを投稿