1

몇 가지 다른 스레드에서 호출 된 경우에도 내 함수 중 하나가 여러 번 실행되는 것을 막으려 고하고 있지만 꽤 어색한 것처럼 보입니다. 여기 동기화 잠금 플래그 및 종료 기능을 확인하는 속기?

는 내가 뭘 내용은 다음과 같습니다. 그것에서의 기능을 sync 블록 return는 해당 폐쇄를 종료하기 때문에

func doSomethingOnceAtATime() { 
    var shouldExit = false 
    DispatchQueue(label: "abcxyz123").sync { 
     guard !inProgress else { shouldExit = true; return } 
     inProgress = true 
    } 
    guard !shouldExit else { return } 

    // Do something 
} 

내가 이런 식으로 할 필요가 있지만 더 스위프 티 '이나 우아하고 있는가 이 일을하는 방법?

답변

1

첫 번째 문제는 다음과 같은 코드가 전혀 동기화를 제공하지 않는다는 것이다 : 새로운 큐에게 당신이 그것을 호출 할 때마다 인스턴스화됩니다

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) 
} 

나는이 지역 변수를 제거한다 동안, 나는 개인적으로 이전 패턴보다 더 나은 생각하지 않는다는 것을 고백해야합니다. 그러나 그것은 당신에게 달려 있습니다.