我搜索过Swift书,但找不到Swift版本的@synchronized。如何在Swift中进行互斥?
答案 0 :(得分:169)
您可以使用GCD。它比@synchronized
更冗长,但作为替代品:
let serialQueue = DispatchQueue(label: "com.test.mySerialQueue")
serialQueue.sync {
// code
}
答案 1 :(得分:168)
我自己一直在寻找这个,并得出结论,这里没有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!
答案 2 :(得分:140)
我喜欢并使用这里的许多答案,所以我选择最适合你的方式。也就是说,当我需要像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
)和&#34;无论程序控制如何转移,都会执行延迟声明中的语句。&#34; 1
答案 3 :(得分:75)
您可以在objc_sync_enter(obj: AnyObject?)
和objc_sync_exit(obj: AnyObject?)
之间添加语句。 @synchronized关键字正在使用这些方法。即。
objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)
答案 4 :(得分:71)
来自Objective-C的@synchronized
指令的模拟可以在Swift中具有任意返回类型和良好的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()
}
答案 5 :(得分:34)
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 }
}
}
}
答案 6 :(得分:23)
要添加返回功能,您可以执行以下操作:
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
}
}
答案 7 :(得分:23)
使用Bryan McLemore的回答,我将其扩展为支持使用Swift 2.0推迟能力投入安全庄园的对象。
func synchronized( lock:AnyObject, block:() throws -> Void ) rethrows
{
objc_sync_enter(lock)
defer {
objc_sync_exit(lock)
}
try block()
}
答案 8 :(得分:13)
在现代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()
}
答案 9 :(得分:10)
Swift 3
此代码具有重新输入功能,可以使用异步函数调用。在此代码中,在调用someAsyncFunc()之后,串行队列上的另一个函数闭包将被处理但被semaphore.wait()阻塞,直到调用signal()。不应该使用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不是一个好主意。
答案 10 :(得分:7)
在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对象发送解锁消息时,必须确保从发送初始锁定消息的同一线程发送消息。从另一个线程解锁锁定可能导致未定义的行为。
答案 11 :(得分:4)
我刚刚在2018年WWDC的“了解崩溃和崩溃日志” session 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
}
}
}
答案 12 :(得分:3)
使用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] = []
然后像往常一样访问变量。
答案 13 :(得分:2)
一个锁可以被同一线程多次获取而没有 导致死锁。
let lock = NSRecursiveLock()
func f() {
lock.lock()
//Your Code
lock.unlock()
}
func f2() {
lock.lock()
defer {
lock.unlock()
}
//Your Code
}
答案 14 :(得分:1)
总之,这里提供了更常见的方法,包括返回值或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()
}
}
答案 15 :(得分:1)
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
}
扩展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)")
}
}
}
答案 16 :(得分:0)
dispatch_barrier_async是更好的方法,但不阻止当前线程。
dispatch_barrier_async(accessQueue,{ 字典[object.ID] =对象 })
答案 17 :(得分:0)
Based on ɲ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
答案 18 :(得分:0)
图我将发布基于先前答案的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
})
答案 19 :(得分:0)
为什么要锁定困难和麻烦? 使用调度障碍。
调度屏障在并发队列中创建同步点。
在它运行时,队列中的其他块都不允许运行,即使它是并发的和其他核心可用。
如果这听起来像一个独占(写)锁,它就是。 非障碍块可以被认为是共享(读)锁。
只要通过队列执行对资源的所有访问,障碍就会提供非常便宜的同步。
答案 20 :(得分:-3)
另一种方法是创建一个超类然后继承它。这样你可以更直接地使用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
}
}