첫 번째 문제는 다음과 같은 코드가 전혀 동기화를 제공하지 않는다는 것이다 : 새로운 큐에게 당신이 그것을 호출 할 때마다 인스턴스화됩니다
DispatchQueue(label: "abcxyz123").sync { ... }
. 대신, 하나의 시리얼 큐 인 속성을 가지고 있어야, 다음
sync
통화의 모두가 하나의 대기열을 사용 로컬 변수의 생성을 좋아하지 않는에 대한 질문에 도착
private let synchronizationQueue = DispatchQueue(label: "abcxyz123")
func someMethodThatNeedsSynchronizedAccess() {
synchronizationQueue.sync { ... }
}
블록 앞에, 나는 그 점에 동조한다. 적어도, 나는 doSomethingOnceAtATime
에서이 패턴을 끌어뿐만 아니라 동기화 로직을 분리하는 경향이있을 것 :
let state = TaskState()
func doSomethingOnceAtATime() {
if !state.attemptSetInProgress() { return }
// Do something
state.unsetInProgress()
}
: 다음
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
훨씬 더 직관적 인 뭔가가된다
그러나 여전히 로컬 변수가 있습니다 (그렇다고해도 더 논리적 인 수준에서 캡슐화 됨). 그게 너를 괴롭히는 것이라면, 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
}
}
}
}
위의 내용은 "두 가지 이상의 상태로 이동 한 경우를 대비하여"일반화 된 것입니다.
그런 다음 당신이 할 수 있습니다
let state = TaskState()
func doSomethingOnceAtATime() {
do { try state.change(to: .inProgress) } catch { return }
// Do something
try? state.change(to: .notInProgress)
}
나는이 지역 변수를 제거한다 동안, 나는 개인적으로 이전 패턴보다 더 나은 생각하지 않는다는 것을 고백해야합니다. 그러나 그것은 당신에게 달려 있습니다.