Programing

스위프트는 Objective-C의“@synchronized”와 어떤 기능을합니까?

lottogame 2020. 4. 25. 09:49
반응형

스위프트는 Objective-C의“@synchronized”와 어떤 기능을합니까?


Swift 책을 검색했지만 @synchronized의 Swift 버전을 찾을 수 없습니다. Swift에서 상호 배제를 어떻게합니까?


GCD를 사용할 수 있습니다. 보다 약간 장황 @synchronized하지만 대체 기능으로 작동합니다.

let serialQueue = DispatchQueue(label: "com.test.mySerialQueue")
serialQueue.sync {
    // code
}

나는 이것을 직접 찾고 있었고 아직 이것에 대한 신속한 내부에 네이티브 구성이 없다는 결론에 도달했습니다.

Matt Bridges 및 다른 코드에서 본 코드 중 일부를 기반 으로이 작은 도우미 기능을 구성했습니다.

func synced(_ lock: Any, closure: () -> ()) {
    objc_sync_enter(lock)
    closure()
    objc_sync_exit(lock)
}

사용법은 매우 간단합니다

synced(self) {
    println("This is a synchronized closure")
}

내가 찾은 문제가 하나 있습니다. 잠금 인수로 배열을 전달하면이 시점에서 매우 혼란스러운 컴파일러 오류가 발생하는 것으로 보입니다. 그렇지 않으면 원하는대로 작동하는 것 같습니다.

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와 같은 것이 필요할 때 선호하는 방법 은 신속한 2에 도입 된 진술을 @synchronized사용합니다 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)

@synchronizedObjective-C 지시문 아날로그는 rethrowsSwift에서 임의의 반환 유형과 훌륭한 동작을 가질 수 있습니다 .

// 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.


스위프트 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()
}

스위프트 3

이 코드는 재 입력 기능이 있으며 비동기 함수 호출과 함께 작동 할 수 있습니다. 이 코드에서 someAsyncFunc ()가 호출 된 후 직렬 큐의 다른 함수 클로저는 처리되지만 signal ()이 호출 될 때까지 semaphore.wait ()에 의해 차단됩니다. 실수하지 않으면 기본 스레드를 차단하므로 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이 지적한 것처럼 올바른 방법은 DispatchQueues와 동기화하는 것입니다.

신속한 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
        }
    }
}

결론적으로, 여기에는 반환 값 또는 void를 포함하고 던지는 일반적인 방법이 있습니다.

수입 재단

extension NSObject {


    func synchronized<T>(lockObj: AnyObject!, closure: () throws -> T) rethrows ->  T
    {
        objc_sync_enter(lockObj)
        defer {
            objc_sync_exit(lockObj)
        }

        return try closure()
    }


}

세부

xCode 8.3.1, 신속한 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
}

전체 샘플

확장 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)")
        }
    }
}

그림 이전 답변으로 작성된 Swift 5 구현을 게시합니다. 고마워요! 값을 반환하는 것이 도움이되므로 두 가지 방법이 있습니다.

먼저 만드는 간단한 수업은 다음과 같습니다.

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
})

시도 : NSRecursiveLock

교착 상태를 유발하지 않고 동일한 스레드에서 여러 번 획득 할 수있는 잠금입니다.

let lock = NSRecursiveLock()

func f() {
    lock.lock()
    //Your Code
    lock.unlock()
}

func f2() {
    lock.lock()
    defer {
        lock.unlock()
    }
    //Your Code
}

왜 자물쇠로 어렵고 번거로워합니까? 디스패치 배리어를 사용하십시오.

디스패치 배리어는 동시 큐 내에 동기화 지점을 작성합니다.

실행 중일 때 동시에 다른 코어를 사용할 수 있어도 큐의 다른 블록을 실행할 수 없습니다.

그것이 독점적 인 (쓰기) 잠금처럼 들린다면 그것은 그렇습니다. 비 배리어 블록은 공유 (읽기) 잠금으로 생각할 수 있습니다.

리소스에 대한 모든 액세스가 큐를 통해 수행되는 한 장벽은 매우 저렴한 동기화를 제공합니다.


유로 버 (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

dispatch_barrier_async가 더 나은 방법이지만 현재 스레드를 차단하지는 않습니다.

dispatch_barrier_async (accessQueue, {dictionary [object.ID] = 객체})


또 다른 방법은 수퍼 클래스를 만든 다음 상속하는 것입니다. 이 방법으로 GCD를 더 직접 사용할 수 있습니다

class Lockable {
    let lockableQ:dispatch_queue_t

    init() {
        lockableQ = dispatch_queue_create("com.blah.blah.\(self.dynamicType)", DISPATCH_QUEUE_SERIAL)
    }

    func lock(closure: () -> ()) {
        dispatch_sync(lockableQ, closure)
    }
}


class Foo: Lockable {

    func boo() {
        lock {
            ....... do something
        }
    }

참고 URL : https://stackoverflow.com/questions/24045895/what-is-the-swift-equivalent-to-objective-cs-synchronized

반응형