2017-12-30 99 views
1

NSKeyedArchiverを使用してアーカイブするタイプSet<CostumObject>のインスタンスがあります。カスタムオブジェクトのセットをスレッドセーフでアーカイブする方法はありますか?

customObject1: CostumObjectcustomObject2: CostumObjectがどこかでインスタンス化されていると仮定します。

私は、次のステートメントを使用する場合:

let setOfCostomObjects: Set<CostumObject> = [customObject1, customObject2] 
let data = NSKeyedArchiver.archivedData(withRootObject: setOfCostomObjects) 

NSKeyedArchiverアーカイブ順次そのプロパティを再帰的にアーカイブされ、両方のカスタムオブジェクトを。

アーカイブ中に別のスレッドがカスタムオブジェクトとそのプロパティの両方を変更できるため、スレッドセーフではありません。

私はスレッドセーフできるアーカイブをカスタムオブジェクトの各プロパティを、同時は以下のようにセットするためのバリアとの同時キューを使用することにより、唯一の単一のセット許可されていますように、と思う

private let concurrentPropertyAccessQueue = DispatchQueue(label: "concurrentPropertyAccessQueue", attributes: .concurrent) 
… 
private var safeProperty = CostumProperty.init() 
public private(set) var property: CostumProperty { 
    get { 
    var result = CostumProperty.init() 
    concurrentPropertyAccessQueue.sync { result = safeProperty } // sync, because result is returned 
    return result 
    } // get 
    set { concurrentPropertyAccessQueue.async(flags: .barrier) { safeProperty = newValue } // executes locked after all gets 
    } // set 
} 
… 
public func threadSafeArchiveOfProperty() -> Data { 
    var data = Data.init() 
    concurrentPropertyAccessQueue.sync { // sync, because result is returned 
     data = NSKeyedArchiver.archivedData(withRootObject: self.safeProperty) 
    } 
    return data 
} 

private let concurrentObjectAccessQueue = DispatchQueue(label: "concurrentObjectAccessQueue", attributes: .concurrent) 
… 
public func encode(with aCoder: NSCoder) { 
    concurrentObjectAccessQueue.async(execute: { 
     aCoder.encode(self.property forKey: "property") 
     … 
    }) 
} 

問題がまだアーカイブカスタムオブジェクトのセットをセーフにスレッドする方法を、次のとおりです。私はまた、同様の方法でスレッドセーフアーカイブ全体のカスタムオブジェクトをすることができると思います。
これにより、アーカイブ中にセットの要素への書き込みアクセスがロックアウトされている必要があります。そうする

一つの方法は、世界的な同時キュー定義することはおそらくです:アーカイブ中にセットし、そのすべての要素をロックするには

public let globalConcurrentAccessQueue = DispatchQueue(label: "globalConcurrentAccessQueue", attributes: .concurrent) 

を、1はおそらくfunc threadSafeArchiveOfSet()を定義Set型に拡張を書くことができます上記のように。
この関数は、globalConcurrentAccessQueueがロックされるように、セットのencode(with aCoder: NSCoder)をオーバーライドします。

これは正しい方法ですか?
これは標準的な解決策が必要な標準的な問題だと思います。

+0

@Robお返事ありがとうございます。私はそれらを理解しなければならず、戻ってくるだろう。 –

+0

@Robプロパティレベルの同期に関して:あなたは完全に正しいです:オブジェクトを全体として一貫性のあるものにするためには、オブジェクトレベルで自身またはそのプロパティを同期させ、プロパティレベルの同期を廃止する必要があります。非同期エンコーディングに関して:あなたはもう一度です:呼び出し元NSKeyedArchiverは、すべての個々のアーカイブ結果が1つの最終データオブジェクトに結合されるため、おそらくエンコーディングがシーケンシャルプロセスであると仮定します。 –

+0

@Robシンクパターンの簡略化について:興味深い!私はこれを知らなかった。あなたの提案はうまくいきますが、[docs](https://developer.apple.com/documentation/dispatch/dispatchqueue/1452870-sync)で見つけられません。同期しないでください戻り値がありますか? –

答えて

0

多くの場合、プロパティレベルの同期は単に不十分です。個々のプロパティへのスレッドセーフなアクセスを提供しますが、異なるプロパティ間の相互依存性が存在するより広いオブジェクトへのスレッドセーフなアクセスを保証しません。プロトタイプの例は、姓と名のプロパティを持つPersonオブジェクトです。姓と名の同期の変更は、内部的に一貫性のない状態でキャプチャされているオブジェクトで終わる可能性があります。より高いレベルでオブジェクトを同期させる必要があることがよくあります。そうした場合、プロパティレベルの同期が冗長になります。

いくつかの無関係な観察:

  1. encode方法はないasychronously、同期タスクを実行する必要があります。呼び出し側は、エンコーディングが完了するまでにエンコーディングが完了したものとみなします。なぜ非同期にしたのかもしれないのでしょうか(たとえば、何も明示的に返されていないなど)、何かが返されるかどうかではなく、むしろ、同期されたオブジェクト。この場合は(NSCoderオブジェクトを更新しています)、encodesyncを使用する必要があります。

  2. 変数を初期化するパターンを使用して、そのローカル変数を変更するためにsyncを呼び出してから、その値を戻すという方法を数回使います。例えば。

    func threadSafeArchiveOfProperty() -> Data { 
        var data = Data.init() 
        concurrentPropertyAccessQueue.sync { // sync, because result is returned 
         data = NSKeyedArchiver.archivedData(withRootObject: self.safeProperty) 
        } 
        return data 
    } 
    

    しかしsyncは閉鎖が値を返すつまり場合、syncはあまりにも、それを返しますが、これを簡素化するための良い方法を提供しています。閉鎖が一つだけの行を持っている場合と、あなたも閉鎖に明示的にreturnは必要ありません。

    func threadSafeArchiveOfProperty() -> Data { 
        return concurrentPropertyAccessQueue.sync { // sync, because result is returned 
         NSKeyedArchiver.archivedData(withRootObject: self.safeProperty) 
        } 
    } 
    
0

のBasem Emaraがhereセットにも適用することができ、スレッドセーフ配列のためのソリューション説明しました:

彼はSynchronizedArrayを通常の配列と似ていると宣言しました。そこには、プライベートな同時キューと配列が含まれていて、配列のプロパティとメソッドのいくつかが公開されています。
可変アクセスはバリアと非同期的に、すなわちキュー内の他のすべてのブロックが終了した後に同期して同時に実行されます。

関連する問題