LoginSignup
2
2

More than 5 years have passed since last update.

lockステートメントで注意すべきコト

Last updated at Posted at 2016-11-19

概要

lockステートメントを利用した相互排他ロックを利用する際に留意すべき点をいくつかまとめてみました。

また、lock(<obj>)<obj>の部分を以下ロックトークン(Lock token)と呼称することにします。

型引数を利用した場合

メソッドなどのパラメータによって貰ってきたエンティティをロックトークンにすることはデッドロックをはじめとする厄介ゴトの根源みたいなもんなので避けるか使うにしても相当注意すべきでしょうが、まぁサンプルなので以下のようなメソッドが有るとします。

以下のようなメソッドを考えてみます。


static void LockSample<T>(T lockToken)
{
    lock (lockToken)
    {
        foreach (var c in "Lock!")
        {
            Console.Write(c);
            Thread.Sleep(10);
        }
        Console.WriteLine();
    }
}

パラメータ使ったロックは色々と問題がありますが、まぁ話を単純化するってコトでご理解下さい。
こいつを、以下のように呼び出したとします。

static void Main(string[] args)
{
    object lockToken=new object();

    Parallel.For(0, 10, i => { LockSample(lockToken); });
}

この場合、想定通り、”Lock!”というキャラクタがコンソールに10個出て終了となります。
他方、下記の場合は想定外の結果になると思います。


static void Main(string[] args)
{
    int lockToken = 10;

    Parallel.For(0, 10, i => { LockSample(lockToken); });
}

実行結果は神のみぞ知るとしか言い様がありませんが、あらかた想定通りとは行かないと思ったり。

なんでこんなことになるのか?

本来、ロックトークンは参照型であることが条件なので、以下のようなコードはメソッド名通りCS0185が発生しちまう。


static void OccurCS0185(int i)
{
    lock (i)
    {
        foreach (var c in "Lock!")
        {
            Console.Write(c);
            Thread.Sleep(10);
        }
        Console.WriteLine();
    }
}

けれど、型引数を利用した場合、型引数にwhere T:classのような制約無しで、コンパイルは通るし、実行も出来ちまう。
その結果、Tが値型になった場合、本来コンパイルすら出来ないのにコンパイルも実行も可能になってしまい、結果が破綻することになる。
ちなみに、なぜコンパイル出来ちまうかというと、先のLockSampleはばらしてみると概ね以下のような状況になってるから。

static void LockSampleReveal<T>(T lockToken)
{
    object token = lockToken;

    lock (token)
    {
        foreach (var c in "Lock!")
        {
            Console.Write(c);
            Thread.Sleep(10);
        }
        Console.WriteLine();
    }
}

一度、objecdt型のローカル変数でlockToekenを受けているので、参照型という条件を満たしている。但し、Tが値型の場合、tokenへの代入は、ボックス化が伴うので、呼び出し毎に別個のエンティティが作成されてしまいlockが用をなさなくなっちまう。

この辺、実はC#6のRCではコンパイルエラーになってくれていた。けれどリリースでRejectされちまった事実があるので、Microsoftも問題を認識しつつも、その影響のでかさゆえ手を着けることができないんじゃ無いかなとか思ってる。

まとめと蛇足

このように、型引数をロックトークンとしてしまった場合、意味を成さないロックになってしまうことが有るので、注意が必要となるかなと。

また、剥き身で使うことはそうそう無いだろうけど、Monitor.Enterはロックトークンの型がobjectなので、基本何でも入るけど結果は先ほどと同じなので、その辺も注意した方が良いかなと思います。

2
2
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
2
2