Bil*_*ill 217 concurrency mutex swift
我搜索过Swift书,但找不到Swift版本的@synchronized.如何在Swift中进行互斥?
con*_*gan 174
使用GCD.它比一个更冗长@synchronized
,但作为替代品非常好用:
let serialQueue = DispatchQueue(label: "com.test.mySerialQueue")
serialQueue.sync {
// code
}
Run Code Online (Sandbox Code Playgroud)
Bry*_*ore 171
我自己一直在寻找这个,并得出结论,这里还没有swift内部的原生构造.
我根据我从Matt Bridges和其他人那里看到的一些代码构建了这个小帮助函数.
func synced(_ lock: Any, closure: () -> ()) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}
Run Code Online (Sandbox Code Playgroud)
用法很简单
synced(self) {
println("This is a synchronized closure")
}
Run Code Online (Sandbox Code Playgroud)
我发现有一个问题.传递一个数组作为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!
Run Code Online (Sandbox Code Playgroud)
ɲeu*_*urɳ 145
我喜欢并使用这里的许多答案,所以我会选择最适合你的方法.也就是说,当我需要像objective-c这样的东西时,我喜欢的方法@synchronized
使用defer
swift 2中引入的语句.
{
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
//
// code of critical section goes here
//
} // <-- lock released when this block is exited
Run Code Online (Sandbox Code Playgroud)
这个方法的好处是,你的关键部分可以退出所希望的任何方式包含块(例如return
,break
,continue
,throw
),和"defer语句中的语句,无论程序控制的传输方式执行." 1
Mat*_*ges 78
您可以与三明治报表objc_sync_enter(obj: AnyObject?)
和objc_sync_exit(obj: AnyObject?)
.@synchronized关键字正在使用这些方法.即
objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)
Run Code Online (Sandbox Code Playgroud)
wer*_*ver 74
在模拟@synchronized
从Objective-C的指令可以有任意的返回类型和漂亮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()
}
Run Code Online (Sandbox Code Playgroud)
使用该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()
}
Run Code Online (Sandbox Code Playgroud)
基于GNewc [1](我喜欢任意返回类型)和Tod Cunningham [2](我喜欢的地方defer
)的答案.
Seb*_*ldt 38
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 }
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 23
要添加返回功能,您可以这样做:
func synchronize<T>(lockObj: AnyObject!, closure: ()->T) -> T
{
objc_sync_enter(lockObj)
var retVal: T = closure()
objc_sync_exit(lockObj)
return retVal
}
Run Code Online (Sandbox Code Playgroud)
随后,您可以使用以下方法调用它:
func importantMethod(...) -> Bool {
return synchronize(self) {
if(feelLikeReturningTrue) { return true }
// do other things
if(feelLikeReturningTrueNow) { return true }
// more things
return whatIFeelLike ? true : false
}
}
Run Code Online (Sandbox Code Playgroud)
Tod*_*ham 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()
}
Run Code Online (Sandbox Code Playgroud)
Sté*_*uca 20
在现代 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()
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它,以利用返回值功能:
let returnedValue = synchronized(self) {
// Your code here
return yourCode()
}
Run Code Online (Sandbox Code Playgroud)
或者像这样:
synchronized(self) {
// Your code here
yourCode()
}
Run Code Online (Sandbox Code Playgroud)
Han*_*nny 10
斯威夫特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()
}
}
Run Code Online (Sandbox Code Playgroud)
没有错误处理,objc_sync_enter/objc_sync_exit不是一个好主意.
随着Swift 并发的出现,我们将使用actor。
\n\n\n您可以使用任务将程序分解为独立的、并发的\n片段。任务彼此隔离,这使得它们同时运行\n是安全的,但有时您需要在任务之间共享\n一些信息。Actor 让您可以在并发代码之间安全地共享\n信息。
\n与类一样,参与者也是引用类型,因此类中值类型和引用类型的比较都是引用类型适用于参与者和类。与类不同,参与者一次只允许一个任务访问其可变状态,这使得多个任务中的代码可以安全地与参与者的同一实例进行交互。例如,这里的 xe2x80x99 是一个记录温度的 actor:
\nRun Code Online (Sandbox Code Playgroud)\nactor TemperatureLogger {\n let label: String\n var measurements: [Int]\n private(set) var max: Int\n\n init(label: String, measurement: Int) {\n self.label = label\n self.measurements = [measurement]\n self.max = measurement\n }\n}\n
您可以使用关键字引入一个参与者
\nactor
,后面是一对大括号中的定义。ActorTemperatureLogger
具有 Actor 外部的其他代码可以访问的属性,并限制 max 属性,因此只有 Actor 内部的代码可以更新最大值。
有关更多信息,请参阅 WWDC 视频使用 Swift Actor 保护可变状态。
\n为了完整起见,历史替代方案包括:
\nGCD串行队列:这是一种简单的预并发方法,可确保一次只有一个线程与共享资源交互。
\n具有并发 GCD 队列的读写器模式:在读写器模式中,使用并发调度队列来执行同步但并发的读取(但仅与其他读取并发,而不是写入),但使用屏障异步执行写入(强制写入)不与该队列上的其他任何操作同时执行)。与简单的 GCD 串行解决方案相比,这可以提供性能改进,但在实践中,这种优势是有限的,并且是以额外的复杂性为代价的(例如,您必须小心线程爆炸场景)。恕我直言,我倾向于避免这种模式,要么坚持串行队列模式的简单性,要么当性能差异至关重要时,使用完全不同的模式。
\n锁:在我的 Swift 测试中,基于锁的同步往往比任何一种 GCD 方法都要快得多。锁有几种类型:
\nNSLock
是一种很好的、相对高效的锁定机制。NSLock
恕我直言,我更喜欢简单的NSRecursiveLock
。递归锁容易被滥用,并且通常会出现代码异味。从技术上讲,可以使用信号量进行同步,但它往往是所有替代方案中最慢的。
\n我在这里概述了一些基准测试结果。
\n简而言之,现在我将 Actor 用于当代代码库,将 GCD 串行队列用于简单场景非异步等待代码,并在那些性能至关重要的罕见情况下使用锁。
\n而且,不用说,我们经常尝试完全减少同步次数。如果可以的话,我们经常使用值类型,其中每个线程都有自己的副本。在无法避免同步的情况下,我们会尝试尽可能减少同步的数量。
\n在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()
Run Code Online (Sandbox Code Playgroud)
警告NSLock类使用POSIX线程来实现其锁定行为.向NSLock对象发送解锁消息时,必须确保从发送初始锁定消息的同一线程发送消息.从其他线程解锁锁定可能导致未定义的行为.
可以被同一个线程多次获取而不会导致死锁的锁。
let lock = NSRecursiveLock()
func f() {
lock.lock()
//Your Code
lock.unlock()
}
func f2() {
lock.lock()
defer {
lock.unlock()
}
//Your Code
}
Run Code Online (Sandbox Code Playgroud)
Objective-C 同步功能支持递归和可重入代码。一个线程可以以递归方式多次使用单个信号量;其他线程被阻止使用它,直到该线程释放了所有用它获得的锁;也就是说,每个@synchronized() 块都会正常退出或通过异常退出。 来源
我刚刚在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
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
与障碍同时发生,读取是异步的,写入等待先前的请求。
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用 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
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以这样做:
@NCCSerialized var foo: Int = 10
Run Code Online (Sandbox Code Playgroud)
或者
@NCCSerialized var myData: [SomeStruct] = []
Run Code Online (Sandbox Code Playgroud)
然后像往常一样访问变量。
归档时间: |
|
查看次数: |
99198 次 |
最近记录: |