LoginSignup
14
15

More than 5 years have passed since last update.

Double-Checked Lockingによる同期処理

Posted at

オブジェクトが存在しない場合のみ生成したい

ここ数日で実装したBTKInjectorにおいて、インスタンス変数をnilで初期化し、初回アクセス時にオブジェクトを割り当てる必要がでてきました。

すごく単純に書くとこんな感じ。

- (id)get
{
    if(_obj == nil){
        _obj = [self getImpl];
    }
    return _obj;
}

マルチスレッド対応

ロックなしだとマルチスレッドで適切に動作しないので、 @synchronizedを使って同期処理を追加します。

- (id)get
{
    @synchronized(self){
       if(_obj == nil){
           _obj = [self getImpl];
       }
       return _obj;
    }
}

完。。。

ではないですね、今回はDIのProvider実装なので、毎回ロックを取得するのは好ましくありません。

Double-Checked Locking

そこで有名なDouble-Checked Locking。同期処理をする前に一度変数をチェックすることで、初回以外はロックが必要なくなります。

NG!!
- (id)get
{
    if(_obj == nil){
        @synchronized(self){
            if(_obj == nil){
                id obj = [self getImpl];
                _obj = obj;
            }
        }
    }
    return _obj;
}

これでOK。。。

でもないんですよね。そこまで含めて有名だと思います。プログラムは、書かれた順番で実行されるとは限りません。

  1. コンパイラによる最適化
  2. CPUによる最適化

という二つの要素により、_objにオブジェクトが設定されているにも関わらず、そのオブジェクトの初期化が完了していない可能性があります。

対策として下記の二つが必要です。
1. volatile宣言で、コンパイラによるメモリアクセス最適化を抑制
2. OSMemoryBarrierで、CPUがメモリを逐次処理するように強制

というわけで、正しくはこちら。

@implementation BTKInjectorProviderBase{
    id volatile _obj;
}

- (id)get
{
    if(_obj == nil){
        @synchronized(self){
            if(_obj == nil){
                id obj = [self getImpl];
                OSMemoryBarrier();
                _obj = obj;
            }
        }
    }
    OSMemoryBarrier();
    return _obj;
}

OSMemoryBarrierのコストは発生しますが、synchronizeするのに比べればはるかに小さいと思います。検証はしてないですが。

dispatch_onceは使えないのか?

Singletonを作る場合であれば、GCDのdispatch_onceを使うことができます。しかし、dispatch_once_tはかならずglobalもしくはstaticなスコープに置く必要があるため、今回のケースでは使用できません。

The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.

最終的なコード

BTKInjectorProviderBase.m

14
15
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
14
15