概要

いっけね、『Effective Python』でマルチスレッドのやり方を読んだのに、全然試してないや。(『Effective Python』その5 並行性と並列性)あんとき読んだのは次のようなものだったな。

  • Python では GIL(global interpreter lock)なる機能のせいで、ふつー1スレッドしか使えない。(=せいぜい並行実行しかできない)
  • そもそもスレッドは協調がムズいし重いしいいとこない。その問題はコルーチンを使うと回避できる。(よくわからん試してない)
  • 並列実行やろうと思ったら concurrent.futures の ProcesPoolExecutor を使え。

 

流し読みノート

スレッドが欲しかったら Thread を使う(まんまや)

  • というわけでマルチスレッドというのは並列実行のことだ。 C# では「スレッド」を使うことでコードの一部を同時に実行することができる。
    • 当然だけど並列実行は CPU が複数無いとできん。
  • CLR スレッドは殆どの場合 OS スレッドと対応するけど、絶対ではない。
    • CLR は現場の作業負荷を考慮してスレッドの個数を調整するが、この挙動は文書化されていなくて、 .NET のリリースごとに変更されている。
  • 複数スレッドから同時に使用されることを想定したコレクションはスレッドセーフコレクションっていう。
  • スレッドを作るのに必要な System.Threading.Thread クラスは CLR スレッドを表す。
    • 現行スレッドは Thrad.CurrentThread プロパティで取得。
var t1 = new Thread(MyThreadEntryPoint);
var t2 = new Thread(MyThreadEntryPoint);
var t3 = new Thread(MyThreadEntryPoint);
t1.Start("http://...");
t2.Start("http://...");
t3.Start("http://...");

private static void MyThreadEntryPoint(object arg)
{
    string url = (string) arg;

    using (var w = new WebClient())
    {
        string page = w.DownloadString(url);
    }
}
  • スレッドの生成と終了は負荷の高い処理。
    • あー。この話は Python でもあったなー。
  • CLR のもつスレッドプールという機能が、生成コストを抑える。
  • スレッドプールは Task クラス経由で使う。
Task.Factory.StartNew(MyThreadEntryPoint, "http://...");

 

ヤバイ興味ない

ダメだ興味なさすぎて何も頭に入って来ない。せめて、表題になっているワードを軽くまとめとく。ワードを聞いたとき、ああ、なんかアレに関係するヤツでしょ……と軽く連想できる程度を目指す。

  • 複数のスレッドで同じ変数やオブジェクトにアクセスするとき、 lock キーワードを使う。これは内部で Monitor クラスを使用している。
lock (_sync)
{
    // 1. lock の引数に指定された値を引数として Monitor.Enter が呼び出される。
    //    = 別のスレッドがロックをしているかどうか確認する API。
    // 2. ブロック内のコードが実行される。
    // 3. 完了したら Monitor.Exit が呼び出される。
}
  • ロックを非常に短い期間しか保持しない場合は Monitor よか SpinLock クラスがいい。
  • 読み取り、書き込み用のロックが欲しいときは ReaderWriterLockSlim を使う。 ReaderWriterLock というのもあるけれど、これはパフォーマンスに問題があるため使わないこと。
  • 遅延初期化が必要になったときは Lazy<T> クラスや LazyInitializer 静的クラスを使う。

 

小休憩

んー、まあやっぱ必要に迫られない限りはわからねーな。てかこの700ページ級読書、そろそろ息切れしてきたぜ。何ヶ月読んでんだよ。

まーともかく公平な Pythonista としては、 C# は Python とは違ってちゃんと並列実行ができるんだなーやるじゃん! とコメントしておこう。