バリア付きのOSアトミック関数を使用して書き込み/スワップした場合、64ビットプラットフォームで64ビットアトミック値の読み取りは安全ですか?

モジュバ

質問は最新のiOSとmacOSについてです。SwiftのアトミックInt64に次の実装があるとします。

struct AtomicInt64 {

    private var _value: Int64 = 0

    init(_ value: Int64) {
        set(value)
    }

    mutating func set(_ newValue: Int64) {
        while !OSAtomicCompareAndSwap64Barrier(_value, newValue, &_value) { }
    }

    mutating func setIf(expectedValue: Int64, _ newValue: Int64) -> Bool {
        return OSAtomicCompareAndSwap64Barrier(expectedValue, newValue, &_value)
    }

    var value: Int64 { _value }
}

valueアクセサーに注意してください:それは安全ですか?

そうでない場合、値をアトミックにフェッチするにはどうすればよいですか?

また、同じクラスの32ビットバージョンは安全ですか?

編集質問は言語に依存しないことに注意してください。上記は、CPU命令を生成する任意の言語で記述されている可能性があります。

編集2OSAtomicインターフェースは現在非推奨ですが、どのような置き換えでも、ほぼ同じ機能と同じ動作が舞台裏で行われると思います。したがって、32ビット値と64ビット値を安全に読み取ることができるかどうかという問題は依然として存在します。

GitHubとここSOでも循環する誤った実装の3つの注意を編集してください:値の読み取りも安全な方法で行う必要があります(以下のRobの回答を参照)

ロブ

このOSAtomicAPIは非推奨です。ドキュメントにはそれが記載されておらず、Swiftからの警告は表示されませんが、Objective-Cから使用すると、非推奨の警告が表示されます。

'OSAtomicCompareAndSwap64Barrier'は非推奨になりました:iOS10で最初に非推奨になりました-代わりにatomic_compare_exchange_strong()を使用してください

(macOSで作業している場合は、macOS 10.12で非推奨になったことを警告します。)

Swiftで変数をアトミックにインクリメントするにどうすればよいですか?を参照してください


あなたは尋ねました:

OSAtomicインターフェースは現在非推奨ですが、どのような置き換えでも、ほぼ同じ機能と同じ動作が舞台裏で行われると思います。したがって、32ビット値と64ビット値を安全に読み取ることができるかどうかという問題は依然として存在します。

推奨される交換はstdatomic.hです。それは持っているatomic_load方法を、私は直接アクセスするのではなく、それを使用します。


個人的には、を使用しないことをお勧めしますOSAtomicObjective-Cからはstdatomic.h、の使用を検討できますが、Swiftからは、GCDシリアルキュー、GCDリーダー/ライターパターン、またはNSLockベースのアプローチなど、標準の一般的な同期メカニズムの1つを使用することをお勧めします従来の通念では、GCDはロックよりも高速でしたが、最近のすべてのベンチマークでは、現在はその逆であることが示唆されているようです。

したがって、ロックの使用をお勧めします。

struct Synchronized<Value> {
    private var _value: Value
    private var lock = NSLock()

    init(_ value: Value) {
        self._value = value
    }

    var value: Value {
        get { lock.synchronized { _value } }
        set { lock.synchronized { _value = newValue } }
    }

    mutating func synchronized<T>(block: (inout Value) throws -> T) rethrows -> T {
        return try lock.synchronized {
            try block(&_value)
        }
    }
}

この小さな拡張機能(AppleのwithCriticalSection方法に触発された)を使用して、より簡単なNSLock対話を提供します

extension NSLocking {
    func synchronized<T>(block: () throws -> T) rethrows -> T {
        lock()
        defer { unlock() }
        return try block()
    }
}

次に、同期された整数を宣言できます。

var foo = Synchronized<Int>(0)

そして今、私は次のように複数のスレッドからそれを百万回インクリメントすることができます:

DispatchQueue.concurrentPerform(iterations: 1_000_000) { _ in
    foo.synchronized { value in
        value += 1
    }
}

print(foo.value)    // 1,000,000

の同期アクセサメソッドを提供していますがvalue、これは単純なロードとストアのみを対象としています。ロード、インクリメント、ストア全体を1つのタスクとして同期する必要があるため、ここでは使用していません。だから私はこのsynchronized方法を使っています。次のことを考慮してください。

DispatchQueue.concurrentPerform(iterations: 1_000_000) { _ in
    foo.value += 1
}

print(foo.value)    // not 1,000,000 !!!

同期valueアクセサーを使用しているため、妥当に見えます。ただし、同期ロジックのレベルが間違っているため、機能しません。この値のロード、インクリメント、およびストアを個別に同期するのではなく、3つのステップすべてをすべて一緒に同期する必要があります。したがって、前の例に示したように、全体value += 1synchronizedクロージャーにラップして、目的の動作を実現します。

ちなみに、並行性とプロパティラッパーにキューとセマフォを使用しますか?を参照してください。GCDシリアルキュー、GCDリーダーライター、セマフォなど、この種の同期メカニズムの他のいくつかの実装、およびこれらをベンチマークするだけでなく、単純なアトミックアクセサーメソッドがスレッドセーフではないことを示す単体テストの場合。


本当に使用したい場合はstdatomic.h、Objective-Cで実装できます。

//  Atomic.h

@import Foundation;

NS_ASSUME_NONNULL_BEGIN

@interface AtomicInt: NSObject

@property (nonatomic) int value;

- (void)add:(int)value;

@end

NS_ASSUME_NONNULL_END

そして

//  AtomicInt.m

#import "AtomicInt.h"
#import <stdatomic.h>

@interface AtomicInt()
{
    atomic_int _value;
}
@end

@implementation AtomicInt

// getter

- (int)value {
    return atomic_load(&_value);
}

// setter

- (void)setValue:(int)value {
    atomic_store(&_value, value);
}

// add methods for whatever atomic operations you need

- (void)add:(int)value {
    atomic_fetch_add(&_value, value);
}

@end

次に、Swiftでは、次のようなことができます。

let object = AtomicInt()

object.value = 0

DispatchQueue.concurrentPerform(iterations: 1_000_000) { _ in
    object.add(1)
}

print(object.value)    // 1,000,000

明らかに、Objective-Cコードに必要なアトミック操作を追加します(私は実装しただけatomic_fetch_addですが、うまくいけば、それがアイデアを示しています)。

個人的には、従来のSwiftパターンを使用しますが、OSAtomicの代替案を本当に使用したい場合は、これが実装のように見える可能性があります。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ