LoginSignup
27
30

More than 5 years have passed since last update.

C# でマルチスレッドの実装をするなら知っておくべきこと(メモ)

Posted at

C# でマルチスレッドの実装を行ってみて、ハマったことや最初に知っておきたかったことのメモ。

1. マルチスレッドの実装で気づいたこと

1-1. スレッドの数の上限

Task.Run で同時に動くスレッドの数に限りがあり、限界に達するとタスクが空くのを待つことになる。

限界数は下記で確認可能:

ThreadPool.GetMaxThreads(out int workerThreads, out int portThreads);
Console.WriteLine("Worker threads={0}, Completion port threads={1}", workerThreads, portThreads)

上限数は下記で引き揚げ可能:

ThreadPool.SetMaxThreads(20, 20);

1-2. async/await を使うべき

できれば Task.Runasync / await とともに使うべき。
そのほうが CPU を効率よく使うように書きやすい。

1-3. 無限ループには終了条件を

Task.Run で無限ループするなら、終了フラグ見て終了する仕組み必須。
これがないと、単体テストの際にもスレッドが終わらずに困るし、システム終了時にも常に強制終了させることになる。

1-4. Sleep の代わりに Task.Delay

Thread.Sleep(500) のかわりに await Task.Delay(500) をつかうべき。

1-5. Task.Run の前後にログを

Task.Run の前後にログ出力があったほうがいい。
万一、本番で待たされる事態が発生したときにログから追いかけられる。

2. UnitTest(MsTest) の実装で気づいたこと

2-1. テストでの無限ループの扱い

Task.Run で無限ループしていると、テストTask終了時ににそのTask(スレッド)が終わらないため(終わらないけどテストは終了するため)、複数テストを走らせた場合のみ、すぐにスレッドプールの上限に達してしまい、著しくテスト速度が劣化するという現象が発生する。
無限ループ部分はモックしてテストでは動かないようにするか、終了フラグなどで無限ループが終了する仕組みが必要。

2-2. テストは並列実行される

static フィールドなど、テスト間で共有する領域に書き込んでいるなら、テストが並列実行されないようにロックを使った配慮が必要。

例:

using System.Threading;

public class TestsSynchronization
{
    private static readonly object lockObject = new object();

    public static void Lock()
    {
        //テストが同時に走らないためにロックを使う
        Monitor.Enter(lockObject);
        Console.WriteLine("----- Lock Start");
    }

    public static void Unlock()
    {
        //テストが同時に走らないためのロック解放
        Monitor.Exit(lockObject);
        Console.WriteLine("----- Lock End");
    }
}

[TestClass]
public class MainTests
{
    private static readonly object _lock = new object();

    [TestInitialize]
    public void TestInitialize()
    {
        TestsSynchronization.Lock();
    }

    [TestCleanup]
    public void Cleanup()
    {
        TestsSynchronization.Unlock();
    }

2-3. 書き込み可能な static フィールド

static フィールドはテストが終了後も書き換え後の値が残っていることに注意。
必ずテスト実行時に値が初期化されるようでなければならない。

2-2. virtual を使う

メソッドの宣言で virtual にしてないと override できないため、そのメソッドをモックにできない。
そうなると、そのメソッドの中身を含めてテストを書かなければならず、本来テストしたいところと、そうじゃないところが混ざり込み、テストの記述が複雑になってしまう。
依存関係がテストに現れてしまう。

依存を切るために publicprotected なメソッドは基本 virtual にしておいたほうがいい。

27
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
30