Objective-Cで@同期ロック/ロック解除はどのように行われますか?


201

@synchronizedは相互ロックを実現するために「ロック」と「ロック解除」を使用しませんか?では、どのようにしてロック/ロック解除しますか?

次のプログラムの出力は「Hello World」のみです。

@interface MyLock: NSLock<NSLocking>
@end

@implementation MyLock

- (id)init {
    return [super init];
}

- (void)lock {
    NSLog(@"before lock");
    [super lock];
    NSLog(@"after lock");
}

- (void)unlock {
    NSLog(@"before unlock");
    [super unlock];
    NSLog(@"after unlock");
}

@end


int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    MyLock *lock = [[MyLock new] autorelease];
    @synchronized(lock) {
        NSLog(@"Hello World");
    }

    [pool drain];
}


10
initが必要なければ、オーバーライドする必要はありません。メソッドをオーバーライドしない場合、ランタイムは自動的にスーパークラスの実装を呼び出します。
コンスタンティーノツァロウは

3
注意すべき重要なことは、上記のコードは同期されていないということです。lockオブジェクトは、その一つのケースがあることは決してないだろう、すべての呼び出しで作成された@synchronizedブロックは、別のをロックアウトします。そして、これは相互排除がないことを意味します。)もちろん、上記の例はで操作を行っているmainため、とにかく除外するものはありませんが、そのコードを他の場所に盲目的にコピーしないでください。
Hot Licks、2015年

3
このSOページを読んだ後、@ synchronizedをもう少し徹底的に調査し、ブログ投稿を書くことにしました。役に立つかもしれません:rykap.com/objective-c/2015/05/09/synchronized
rjkaplan

回答:


323

Objective-C言語レベルの同期では、同じようにmutexを使用NSLockします。意味的にはいくつかの小さな技術的な違いがありますが、それらを共通の(より原始的な)エンティティの上に実装された2つの個別のインターフェースと考えるのは基本的に正しいことです。

特に、NSLockには明示的なロックがあり@synchronizedますが、同期には、使用するオブジェクトに関連付けられた暗黙的なロックがあります。言語レベルのロックの利点は、コンパイラがスコープレベルの問題を処理できるようにコンパイラが理解できることですが、機械的には基本的に同じように動作します。

@synchronizedコンパイラの書き換えと考えることができます。

- (NSString *)myString {
  @synchronized(self) {
    return [[myString retain] autorelease];
  }
}

に変換されます:

- (NSString *)myString {
  NSString *retval = nil;
  pthread_mutex_t *self_mutex = LOOK_UP_MUTEX(self);
  pthread_mutex_lock(self_mutex);
  retval = [[myString retain] autorelease];
  pthread_mutex_unlock(self_mutex);
  return retval;
}

実際の変換はより複雑であり、再帰的ロックを使用するため、これは正確ではありませんが、ポイントを理解する必要があります。


17
@synchronizedが行う例外処理も忘れてしまいます。そして私が理解しているように、これの多くは実行時に処理されます。これは競合しないロックなどに最適化することができます
クインテイラー

7
私が言ったように、実際に生成されたものはより複雑ですが、DWARF3アンワインドテーブルを構築するためにセクションディレクティブを書く気にはなりませんでした;-)
Louis Gerbarg 09

そして、私はあなたを責めることはできません。:-)また、OS XはDWARFではなくMach-O形式を使用することに注意してください。
クインテイラー

5
バイナリ形式としてDWARFを使用する人はいません。OS XはデバッグシンボルにDWARFを使用し、ゼロコストの例外にDWARF
アン

7
参考までに、Mac OS Xのコンパイラバックエンドを作成しました;-)
Louis Gerbarg、2009

40

Objective-Cでは、@synchronizedブロックが自動的にロックとロック解除(および可能な例外)を処理します。ランタイムは、同期しているオブジェクトに関連付けられているNSRecursiveLockを動的に生成します。このAppleのドキュメントでは、それについて詳しく説明しています。これが、NSLockサブクラスからのログメッセージが表示されない理由です。同期するオブジェクトは、NSLockだけでなく、何でもかまいません。

基本的に@synchronized (...)は、コードを効率化する便利な構成です。ほとんどの単純化する抽象化と同様に、関連するオーバーヘッド(隠れたコストと考えてください)があり、それを認識しておくことは良いことですが、とにかくそのような構成を使用する場合、生のパフォーマンスはおそらく最高の目標ではありません。


1
そのリンクは期限切れです。ここで更新されたリンクです:developer.apple.com/library/archive/documentation/Cocoa/...
アリエル・シュタイナー

31

実は

{
  @synchronized(self) {
    return [[myString retain] autorelease];
  }
}

に直接変換します:

// needs #import <objc/objc-sync.h>
{
  objc_sync_enter(self)
    id retVal = [[myString retain] autorelease];
  objc_sync_exit(self);
  return retVal;
}

このAPIはiOS 2.0以降で利用可能で、次を使用してインポートされます...

#import <objc/objc-sync.h>

だからそれはスローされた例外をきれいに処理するためのサポートを提供しませんか?
ダスティン

これはどこかに文書化されていますか?
jbat100 2012

6
そこにはアンバランスなブレースがあります。
Potatoswatter

@Dustinは実際に、ドキュメントから次のように@synchronized実行します。
Pieter 2013

objc_sync_enterはおそらくpthread mutexを使用するので、Louisの変換はより深く正確です。
ジャック

3

Appleの@synchronizedの実装はオープンソースであり、ここで見つけることができます。Mike ashがこの件に関して2つの非常に興味深い投稿を書いています。

簡単に言えば、オブジェクトポインター(メモリアドレスをキーとして使用)をpthread_mutex_tロックにマップするテーブルがあり、ロックは必要に応じてロックおよびロック解除されます。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.