私はSwiftの本を検索しましたが、Swift版の@synchronizedが見つかりません。 Swiftで相互排除を行うにはどうすればよいですか?
あなたはGCDを使うことができます。これは@synchronized
よりも少し冗長ですが、代わりとして機能します。
let serialQueue = DispatchQueue(label: "com.test.mySerialQueue")
serialQueue.sync {
// code
}
私はこれを自分で探していて、Swiftの内部にはまだネイティブコンストラクトがないという結論に達しました。
私はMatt Bridgesと他の人から見たコードのいくつかに基づいてこの小さなヘルパー関数を作りました。
func synced(_ lock: Any, closure: () -> ()) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}
使い方はかなり簡単です
synced(self) {
println("This is a synchronized closure")
}
私はこれで見つけた一つの問題があります。 lock引数として配列を渡すと、この時点では非常に曖昧なコンパイラエラーが発生するようです。そうでなければそれは望みどおりに動作するようです。
Bitcast requires both operands to be pointer or neither
%26 = bitcast i64 %25 to %objc_object*, !dbg !378
LLVM ERROR: Broken function found, compilation aborted!
私はここで多くの答えを気に入って使っていますので、あなたに最適なものを選んでください。ただし、Objective-cの@synchronized
のようなものが必要なときに私が好む方法は、Swift 2で導入された defer
ステートメントを使用します。
{
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
//
// code of critical section goes here
//
} // <-- lock released when this block is exited
このメソッドのいいところは、あなたのクリティカルセクションは望みの方法(例えばreturn
、break
、continue
、throw
)で包含ブロックを終了できることです。 " 1
objc_sync_enter(obj: AnyObject?)
とobjc_sync_exit(obj: AnyObject?)
の間にステートメントを挟むことができます。 @synchronizedキーワードは、カバーの下でこれらのメソッドを使用しています。すなわち.
objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)
Objective-Cの @synchronized
ディレクティブの類似物は、Swiftでは任意の戻り型とNice rethrows
の動作を持つことができます。
// Swift 3
func synchronized<T>(_ lock: AnyObject, _ body: () throws -> T) rethrows -> T {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
return try body()
}
defer
ステートメントを使用すると、一時変数を導入せずに直接値を返すことができます。
Swift 2では、クロージャに@noescape
属性を追加して、最適化をさらに進めます。
// Swift 2
func synchronized<T>(lock: AnyObject, @noescape _ body: () throws -> T) rethrows -> T {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
return try body()
}
GNewc [1] (ここでは任意の戻り値の型が好きです)およびTod Cunningham [2] (私はdefer
が好きです)の回答に基づいています。
Swift 4
Swift 4では、GCDディスパッチキューを使用してリソースをロックできます。
class MyObject {
private var internalState: Int = 0
private let internalQueue: DispatchQueue = DispatchQueue(label:"LockingQueue") // Serial by default
var state: Int {
get {
return internalQueue.sync { internalState }
}
set (newState) {
internalQueue.sync { internalState = newState }
}
}
}
返品機能を追加するには、次のようにします。
func synchronize<T>(lockObj: AnyObject!, closure: ()->T) -> T
{
objc_sync_enter(lockObj)
var retVal: T = closure()
objc_sync_exit(lockObj)
return retVal
}
それ以降は、次のように呼び出します。
func importantMethod(...) -> Bool {
return synchronize(self) {
if(feelLikeReturningTrue) { return true }
// do other things
if(feelLikeReturningTrueNow) { return true }
// more things
return whatIFeelLike ? true : false
}
}
Bryan McLemoreの回答を使用して、Swift 2.0の遅延機能を使って安全な方法で投下するオブジェクトをサポートするように拡張しました。
func synchronized( lock:AnyObject, block:() throws -> Void ) rethrows
{
objc_sync_enter(lock)
defer {
objc_sync_exit(lock)
}
try block()
}
Swift
このコードには再入力機能があり、非同期関数呼び出しで機能します。このコードでは、someAsyncFunc()が呼び出された後、シリアルキューの別の関数クロージャが処理されますが、signal()が呼び出されるまでsemaphore.wait()によってブロックされます。間違えない限りinternalQueue.syncはメインスレッドをブロックするため、internalQueue.syncは使用しないでください。
let internalQueue = DispatchQueue(label: "serialQueue")
let semaphore = DispatchSemaphore(value: 1)
internalQueue.async {
self.semaphore.wait()
// Critical section
someAsyncFunc() {
// Do some work here
self.semaphore.signal()
}
}
objc_sync_enter/objc_sync_exitはエラー処理なしではお勧めできません。
Swift4では NSLock を使用します。
let lock = NSLock()
lock.lock()
if isRunning == true {
print("Service IS running ==> please wait")
return
} else {
print("Service not running")
}
isRunning = true
lock.unlock()
警告NSLockクラスはPOSIXスレッドを使用してそのロック動作を実装します。 NSLockオブジェクトにロック解除メッセージを送信するときは、メッセージが最初のロックメッセージを送信したのと同じスレッドから送信されていることを確認する必要があります。別のスレッドからロックを解除すると、未定義の動作が発生する可能性があります。
私はちょうど2018年のWWDCの「理解クラッシュとクラッシュログ」 セッション414 で答えを見つけました。 conmulliganが指摘したように正しい方法はsyncと一緒にDispatchQueuesを使うことです。
Swift 4では、次のようになります。
class ImageCache {
private let queue = DispatchQueue(label: "sync queue")
private var storage: [String: UIImage] = [:]
public subscript(key: String) -> UIImage? {
get {
return queue.sync {
return storage[key]
}
}
set {
queue.sync {
storage[key] = newValue
}
}
}
}
障壁と同時に、読み取りは非同期で、書き込みは前の要求を待ちます。
class ImageCache {
private let queue = DispatchQueue(label: "with barriers", attributes: .concurrent)
private var storage: [String: UIImage] = [:]
func get(_ key: String, onResult: @escaping (UIImage?)->Void) {
queue.async { [weak self] in
guard let self = self else { return }
onResult(self.storage[key])
}
}
func set(_ image: UIImage, for key: String) {
queue.async(flags: .barrier) { [weak self] in
guard let self = self else { return }
self.storage[key] = image
}
}
}
xCode 8.3.1、Swift 3.1
異なるスレッドから値を読み取ります(非同期)。
class AsyncObject<T>:CustomStringConvertible {
private var _value: T
public private(set) var dispatchQueueName: String
let dispatchQueue: DispatchQueue
init (value: T, dispatchQueueName: String) {
_value = value
self.dispatchQueueName = dispatchQueueName
dispatchQueue = DispatchQueue(label: dispatchQueueName)
}
func setValue(with closure: @escaping (_ currentValue: T)->(T) ) {
dispatchQueue.sync { [weak self] in
if let _self = self {
_self._value = closure(_self._value)
}
}
}
func getValue(with closure: @escaping (_ currentValue: T)->() ) {
dispatchQueue.sync { [weak self] in
if let _self = self {
closure(_self._value)
}
}
}
var value: T {
get {
return dispatchQueue.sync { _value }
}
set (newValue) {
dispatchQueue.sync { _value = newValue }
}
}
var description: String {
return "\(_value)"
}
}
print("Single read/write action")
// Use it when when you need to make single action
let obj = AsyncObject<Int>(value: 0, dispatchQueueName: "Dispatch0")
obj.value = 100
let x = obj.value
print(x)
print("Write action in block")
// Use it when when you need to make many action
obj.setValue{ (current) -> (Int) in
let newValue = current*2
print("previous: \(current), new: \(newValue)")
return newValue
}
extension DispatchGroup
extension DispatchGroup {
class func loop(repeatNumber: Int, action: @escaping (_ index: Int)->(), completion: @escaping ()->()) {
let group = DispatchGroup()
for index in 0...repeatNumber {
group.enter()
DispatchQueue.global(qos: .utility).async {
action(index)
group.leave()
}
}
group.notify(queue: DispatchQueue.global(qos: .userInitiated)) {
completion()
}
}
}
クラスViewController
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//sample1()
sample2()
}
func sample1() {
print("=================================================\nsample with variable")
let obj = AsyncObject<Int>(value: 0, dispatchQueueName: "Dispatch1")
DispatchGroup.loop(repeatNumber: 5, action: { index in
obj.value = index
}) {
print("\(obj.value)")
}
}
func sample2() {
print("\n=================================================\nsample with array")
let arr = AsyncObject<[Int]>(value: [], dispatchQueueName: "Dispatch2")
DispatchGroup.loop(repeatNumber: 15, action: { index in
arr.setValue{ (current) -> ([Int]) in
var array = current
array.append(index*index)
print("index: \(index), value \(array[array.count-1])")
return array
}
}) {
print("\(arr.value)")
}
}
}
結論として、ここでは戻り値や無効を含むより一般的な方法を示し、
輸入財団
extension NSObject {
func synchronized<T>(lockObj: AnyObject!, closure: () throws -> T) rethrows -> T
{
objc_sync_enter(lockObj)
defer {
objc_sync_exit(lockObj)
}
return try closure()
}
}
図前の回答から構築されたSwift 5実装を投稿します。みんなありがとう!値を返すものもあると便利なので、2つの方法があります。
最初に作成する簡単なクラスを次に示します。
import Foundation
class Sync {
public class func synced(_ lock: Any, closure: () -> ()) {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
closure()
}
public class func syncedReturn(_ lock: Any, closure: () -> (Any?)) -> Any? {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
return closure()
}
}
戻り値が必要な場合は、次のように使用します。
return Sync.syncedReturn(self, closure: {
// some code here
return "hello world"
})
または:
Sync.synced(self, closure: {
// do some work synchronously
})
なぜそれを困難にしてロックに面倒なのでしょうか。ディスパッチバリアを使用する.
ディスパッチバリアは、コンカレントキュー内に同期ポイントを作成します。
実行中は、コンカレントコアや他のコアが使用可能であっても、キュー上の他のブロックは実行できません。
それが排他的(書き込み)ロックのように聞こえるならば、それはそうです。非バリアブロックは、共有(読み取り)ロックと見なすことができます。
リソースへのすべてのアクセスがキューを介して実行される限り、バリアは非常に安価な同期を提供します。
現在のスレッドをブロックしない一方で、dispatch_barrier_asyncがより良い方法です。
dispatch_barrier_async(accessQueue、{dictionary [object.ID] = object})
ɲeuroburɳに基づく 、サブクラスのケースをテストする
class Foo: NSObject {
func test() {
print("1")
objc_sync_enter(self)
defer {
objc_sync_exit(self)
print("3")
}
print("2")
}
}
class Foo2: Foo {
override func test() {
super.test()
print("11")
objc_sync_enter(self)
defer {
print("33")
objc_sync_exit(self)
}
print("22")
}
}
let test = Foo2()
test.test()
1
2
3
11
22
33
試してください: NSRecursiveLock
デッドロックを引き起こすことなく、同じスレッドによって複数回取得されるロック。
let lock = NSRecursiveLock()
func f() {
lock.lock()
//Your Code
lock.unlock()
}
func f2() {
lock.lock()
defer {
lock.unlock()
}
//Your Code
}