Swift 책을 검색했지만 @synchronized의 Swift 버전을 찾을 수 없습니다. Swift에서 상호 배제를 어떻게합니까?
removeFirst()
합니까?
Swift 책을 검색했지만 @synchronized의 Swift 버전을 찾을 수 없습니다. Swift에서 상호 배제를 어떻게합니까?
removeFirst()
합니까?
답변:
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!
@synchronized
좋게 블록을하지만, 참고 그것은 같은 실제 내장 블록 문 동일하지 않은 것을 @synchronized
때문에, 목표 - C의 블록 return
과 break
같은 주변 기능 / 루프 밖으로 뛰어 더 이상 작동 문 이것이 평범한 진술이라면 가능할 것입니다.
defer
을 보장하기 위해 새 키워드를 사용하는 것이 좋습니다 . objc_sync_exit
closure
나는 여기에 많은 답변을 좋아하고 사용하므로 가장 적합한 것을 선택하십시오. 즉, 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
lock
? lock
초기화 방법
lock
objective-c 객체입니다.
objc_sync_enter(obj: AnyObject?)
와 사이에 문을 삽입 할 수 있습니다 objc_sync_exit(obj: AnyObject?)
. @synchronized 키워드는 표지 아래 해당 방법을 사용하고 있습니다. 즉
objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)
objc_sync_enter
및 objc_sync_exit
Objc - sync.h에 정의 된 방법이며, 오픈 소스 : opensource.apple.com/source/objc4/objc4-371.2/runtime/...
objc_sync_enter(…)
& objc_sync_exit(…)
등 아이폰 OS / 맥 OS / 기관이 제공 한 공개 헤더이다. API ( ….sdk
경로 안에있는 것처럼 보입니다 usr/include/objc/objc-sync.h
) . 공개 API인지 여부를 확인하는 가장 쉬운 방법은 (Xcode에서) 함수 이름을 입력하는 것입니다 (예 : objc_sync_enter()
C 함수에 인수를 지정할 필요가 없음) . 해당 API의 헤더 파일 이 표시되면 공개입니다 (헤더가 공개되지 않은 경우 헤더를 볼 수 없으므로) .
@synchronized
Objective-C 의 지시문 아날로그는 rethrows
Swift에서 임의의 반환 유형과 훌륭한 동작을 가질 수 있습니다 .
// 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 }
}
}
}
.serial
사용할 수없는 것 같습니다. 그러나 .concurrent
가능합니다. : /
myObject.state = myObject.state + 1
동시에 실행 하는 경우 총 작업 수를 계산하지 않고 비 결정적 값을 산출합니다. 이 문제를 해결하려면 읽기 및 쓰기가 원자 적으로 발생하도록 호출 코드를 직렬 큐에 래핑해야합니다. 물론 Obj-c도 @synchronised
같은 문제가 있으므로 그런 의미에서 구현이 정확합니다.
myObject.state += 1
읽기와 쓰기 작업의 조합입니다. 다른 스레드는 값을 설정 / 쓰기 위해 여전히 사용될 수 있습니다. 당으로 objc.io/blog/2018/12/18/atomic-variables ,을 실행하는 것이 더 쉽습니다 set
변수 자체에서 대신이 아닌 동기 블록 / 폐쇄에.
반환 기능을 추가하려면 다음을 수행하십시오.
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는 오류 처리 없이는 좋은 생각이 아닙니다.
2018 WWDC의 "충돌 및 충돌 로그 이해" 세션 414 에서 동기화와 함께 DispatchQueue를 사용하여 다음과 같은 방법을 보여줍니다.
신속한 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) -> UIImage? {
return queue.sync { [weak self] in
guard let self = self else { return nil }
return 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
}
}
}
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 객체로 보낼 때 초기 잠금 메시지를 보낸 동일한 스레드에서 메시지가 보내 졌는지 확인해야합니다. 다른 스레드에서 잠금을 잠금 해제하면 동작이 정의되지 않을 수 있습니다.
최신 Swift 5에서 리턴 기능 :
/**
Makes sure no other thread reenters the closure before the one running has not returned
*/
@discardableResult
public func synchronized<T>(_ lock: AnyObject, closure:() -> T) -> T {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
return closure()
}
리턴 값 기능을 이용하려면 다음과 같이 사용하십시오.
let returnedValue = synchronized(self) {
// Your code here
return yourCode()
}
아니면 그렇지 않으면 :
synchronized(self) {
// Your code here
yourCode()
}
GCD
). 본질적으로 아무도 사용 방법을 사용하거나 이해하지 못하는 것 같습니다 Thread
. 나는 그것에 행복하다-반면 GCD
에 어려움과 한계로 가득하다.
시도 : NSRecursiveLock
교착 상태를 유발하지 않고 동일한 스레드에서 여러 번 획득 할 수있는 잠금입니다.
let lock = NSRecursiveLock()
func f() {
lock.lock()
//Your Code
lock.unlock()
}
func f2() {
lock.lock()
defer {
lock.unlock()
}
//Your Code
}
그림 이전 답변으로 작성된 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
})
public class func synced<T>(_ lock: Any, closure: () -> T)
는 void 및 다른 유형 모두에서 작동합니다. 재성장 물건도 있습니다.
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의 속성 래퍼를 사용하면 이것이 현재 사용중인 것입니다.
@propertyWrapper public struct NCCSerialized<Wrapped> {
private let queue = DispatchQueue(label: "com.nuclearcyborg.NCCSerialized_\(UUID().uuidString)")
private var _wrappedValue: Wrapped
public var wrappedValue: Wrapped {
get { queue.sync { _wrappedValue } }
set { queue.sync { _wrappedValue = newValue } }
}
public init(wrappedValue: Wrapped) {
self._wrappedValue = wrappedValue
}
}
그럼 당신은 할 수 있습니다 :
@NCCSerialized var foo: Int = 10
또는
@NCCSerialized var myData: [SomeStruct] = []
그런 다음 평소처럼 변수에 액세스하십시오.
DispatchQueue
하면 사용자에게 숨겨진 것을 만드는 부작용 이 있습니다. 이 SO 참조가 마음을 편안하게하는 것을 발견했습니다. stackoverflow.com/a/35022486/1060314
왜 어렵고 자물쇠가 번거 롭습니까? 디스패치 배리어를 사용하십시오.
디스패치 장벽은 동시 큐 내에 동기화 지점을 작성합니다.
실행 중일 때 동시에 다른 코어를 사용할 수 있어도 큐의 다른 블록을 실행할 수 없습니다.
그것이 독점적 인 (쓰기) 잠금처럼 들린다면 그것은 그렇습니다. 비 배리어 블록은 공유 (읽기) 잠금으로 생각할 수 있습니다.
리소스에 대한 모든 액세스가 큐를 통해 수행되는 한 장벽은 매우 저렴한 동기화를 제공합니다.
유로 버 (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
또 다른 방법은 수퍼 클래스를 만든 다음 상속하는 것입니다. 이런 식으로 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
}
}