Shorthand 检查同步锁定标志和退出函数?
Shorthand to check sync lock flag and exit function?
我试图阻止我的一个函数多次 运行 即使从几个不同的线程调用,但它看起来很笨拙。
这是我正在做的事情:
func doSomethingOnceAtATime() {
var shouldExit = false
DispatchQueue(label: "abcxyz123").sync {
guard !inProgress else { shouldExit = true; return }
inProgress = true
}
guard !shouldExit else { return }
// Do something
}
我必须这样做,因为 sync
块中的 return
只退出那个闭包,而不是它所在的函数。有没有更快捷或更优雅的方法来做到这一点?
第一个问题是以下代码根本不提供同步:
DispatchQueue(label: "abcxyz123").sync { ... }
这将在您每次调用它时实例化一个新队列。相反,你应该有一个 属性 这是一个单一的串行队列,然后在你所有的 sync
调用中使用这个单一队列:
private let synchronizationQueue = DispatchQueue(label: "abcxyz123")
func someMethodThatNeedsSynchronizedAccess() {
synchronizationQueue.sync { ... }
}
关于你不喜欢在块之前创建局部变量的问题,我很同情这一点。至少,我倾向于将这种模式从 doSomethingOnceAtATime
中分离出来,并隔离同步逻辑:
class TaskState {
private var inProgress = false
private let queue = DispatchQueue(label: "...")
func attemptSetInProgress() -> Bool {
var succeed = false
queue.sync {
if !inProgress {
succeed = true
inProgress = true
}
}
return succeed
}
func unsetInProgress() {
queue.sync {
inProgress = false
}
}
}
然后,您的 doSomethingOnceAtATime
变得更加直观:
let state = TaskState()
func doSomethingOnceAtATime() {
if !state.attemptSetInProgress() { return }
// Do something
state.unsetInProgress()
}
但这仍然有局部变量(尽管不可否认,封装在更合乎逻辑的级别)。如果连这都困扰你,我们可以利用 sync
方法 rethrows
这一事实来消除它。所以我们可以这样做:
class TaskState {
enum TaskStateError: Error {
case alreadyInProgress
}
enum State {
case inProgress
case notInProgress
}
private var state = State.notInProgress
private let syncQueue = DispatchQueue(label: "sync")
/// Try changing task status, if we can.
///
/// - Note: Throw error if state already "in progress" and trying to change it to "in progress" again.
func change(to newState: State) throws {
try syncQueue.sync {
if state == .inProgress && newState == .inProgress {
throw TaskStateError.alreadyInProgress
} else {
state = newState
}
}
}
}
请注意,以上内容还概括了 "state"(以防您搬到过两个以上的州)。
那么你可以这样做:
let state = TaskState()
func doSomethingOnceAtATime() {
do { try state.change(to: .inProgress) } catch { return }
// Do something
try? state.change(to: .notInProgress)
}
我必须承认,虽然这摆脱了局部变量,但我个人认为它并不比之前的模式更好。但这取决于你。
我试图阻止我的一个函数多次 运行 即使从几个不同的线程调用,但它看起来很笨拙。
这是我正在做的事情:
func doSomethingOnceAtATime() {
var shouldExit = false
DispatchQueue(label: "abcxyz123").sync {
guard !inProgress else { shouldExit = true; return }
inProgress = true
}
guard !shouldExit else { return }
// Do something
}
我必须这样做,因为 sync
块中的 return
只退出那个闭包,而不是它所在的函数。有没有更快捷或更优雅的方法来做到这一点?
第一个问题是以下代码根本不提供同步:
DispatchQueue(label: "abcxyz123").sync { ... }
这将在您每次调用它时实例化一个新队列。相反,你应该有一个 属性 这是一个单一的串行队列,然后在你所有的 sync
调用中使用这个单一队列:
private let synchronizationQueue = DispatchQueue(label: "abcxyz123")
func someMethodThatNeedsSynchronizedAccess() {
synchronizationQueue.sync { ... }
}
关于你不喜欢在块之前创建局部变量的问题,我很同情这一点。至少,我倾向于将这种模式从 doSomethingOnceAtATime
中分离出来,并隔离同步逻辑:
class TaskState {
private var inProgress = false
private let queue = DispatchQueue(label: "...")
func attemptSetInProgress() -> Bool {
var succeed = false
queue.sync {
if !inProgress {
succeed = true
inProgress = true
}
}
return succeed
}
func unsetInProgress() {
queue.sync {
inProgress = false
}
}
}
然后,您的 doSomethingOnceAtATime
变得更加直观:
let state = TaskState()
func doSomethingOnceAtATime() {
if !state.attemptSetInProgress() { return }
// Do something
state.unsetInProgress()
}
但这仍然有局部变量(尽管不可否认,封装在更合乎逻辑的级别)。如果连这都困扰你,我们可以利用 sync
方法 rethrows
这一事实来消除它。所以我们可以这样做:
class TaskState {
enum TaskStateError: Error {
case alreadyInProgress
}
enum State {
case inProgress
case notInProgress
}
private var state = State.notInProgress
private let syncQueue = DispatchQueue(label: "sync")
/// Try changing task status, if we can.
///
/// - Note: Throw error if state already "in progress" and trying to change it to "in progress" again.
func change(to newState: State) throws {
try syncQueue.sync {
if state == .inProgress && newState == .inProgress {
throw TaskStateError.alreadyInProgress
} else {
state = newState
}
}
}
}
请注意,以上内容还概括了 "state"(以防您搬到过两个以上的州)。
那么你可以这样做:
let state = TaskState()
func doSomethingOnceAtATime() {
do { try state.change(to: .inProgress) } catch { return }
// Do something
try? state.change(to: .notInProgress)
}
我必须承认,虽然这摆脱了局部变量,但我个人认为它并不比之前的模式更好。但这取决于你。