2017-12-29 53 views
3

화면 S를 상상해보십시오. 사용자가 S에 도착하면 물건을보십시오. 버튼 B ... DispatchSemaphore를이 "starting busy"투기 프로세스 시나리오에서 사용하십시오.

| | 
| B| 
| | 
| | 

당신이 B 누르면

이 ..
func clickedB() { 

    blockingSpinner = true 
    longCalculation() 
    blockingSpinner = false 
    showResult() 
} 

func longCalculation() { 

    // a few seconds 
} 

있다 (경우 계산이 일어나고있는 동안 우리는 /, 모달 스피너를보고, 사용자는 단지 기다리고 싶지는.) 사용자가 스크린 S에 도착했을 때

일반적으로, 그들은 B.

그래서를 ... 만지기 전에 몇 초에 뭔가 다른 볼

var waitor = DispatchSemaphore(value: 0) // or ??? 

func viewDidLoad() { 

    DispatchQueue.global(qos: .background).async { longCalculation() } 
} 
func longCalculation() { 

    something waitor 
    do the calculation 
    something waitor 
    DispatchQueue.main.async { 
    something waitor 
    } 
} 
func clickedB() { 

    // (note that ... calculation may have finished ages ago 
    // or we may be in the middle of it, it has a second or so remaining 
    // or perhaps even this is the second+ time the user has clicked B) 
    something waitor 
    if/while longCalculation is still running, 
     blockingSpinner = true 
    blockingSpinner = false 
    showResult() 
} 

나는이 시나리오에서 DispatchSemaphore을 어떻게 사용하는지 전혀 모른다.

그들이 만든 구체적인 방법은 wait()signal()의 작업을 여기에 추가하지 않는 것 같습니다.

이 시나리오에서 DispatchSemaphore을 사용하는 방법은 무엇입니까?

+0

아래의 답변은 Josh가 SO 네트워크 전체에 대한 단일 답변 중 하나 여야합니다. 오크. – Fattie

답변

1

한 번에 한 가지만 진행하면 세마포어 값이 1이되어야합니다. 따라서 NSLock을 쉽게 사용할 수 있습니다. 그러나 여기에 세마포어를 사용하는 개요가 있습니다.

계산을 시작하면 wait() (무기한)이 세마포어에 있습니다. 제안했듯이 View Controller 수명주기의 고유 한 순서를 이용하면 이것이 실제로 차단되지 않는다는 것을 알 수 있습니다. 끝낼 때마다`signal(). 분명히이 작업은 백그라운드에서 수행되어야 주 스레드가 차단되지 않습니다.

단추 탭을 처리 할 때 즉시 대기 시간을 가진 "대기"로 세마포어를 테스트하고 그 결과로 .timedOut이 표시되면 회 전자를 표시하십시오. 세마포어가 사용 가능한지 또는 시간 초과되는지에 따라 실제 대기 시간이 없으므로 주 스레드에서이 작업을 수행 할 수 있습니다. 여기에 신호가 필요 없다는 것을 유의하십시오 : 대기 중 이 시간 초과되면으로 세마포어가 자동으로 다시 증가합니다. 대기가 성공하면 작업이 완료되고 직접 결과가 표시됩니다.

작업이 완료되지 않은 경우 회 전자가 표시됩니다. 이제 백그라운드에서 (무기한으로) 기다립니다. 세마포어가 신호 되었기 때문에 대기가 끝나면 주 스레드로 다시 건너 뛰고 회 전자를 닫고 결과를 표시합니다.

이 시점에서 세마포어의 개수는 0이므로이 코드 경로를 다시 거쳐야 할 경우 세마포어에 신호를 보내야합니다.

기본적으로, 코드 스케치를 작성하면 제작 :

class WhateverViewController : UIViewController 
{ 
    private let semaphore = DispatchSemaphore(value: 1) 

    override func viewDidLoad() 
    { 
     super.viewDidLoad() 
     self.performLongCalculation() 
    } 

    private func performLongCalculation() 
    { 
     DispatchQueue.global(qos: .background).async { 
      self.semaphore.wait() 
      // Synchronous processing... 
      self.semaphore.signal() 
     } 
    } 

    private func buttonTapped() 
    { 
     if self.semaphore.isBusy { 
      self.waitForResult() 
     } 
     else { 
      self.showResult() 
     } 
    } 

    private func buttonTappedAlternative() 
    { 
     // Show the spinner unconditionally, if you assume that the 
     // calculation isn't already done. 
     self.waitForResult() 
    } 

    private func waitForResult() 
    { 
     self.showSpinner() 
     DispatchQueue.global(qos: .userInitiated).async { 
      self.semaphore.wait() 
      DispatchQueue.main.async { 
       self.dismissSpinner() 
       self.showResult() 
      } 
     } 
    } 

    private func showResult() 
    { 
     // Put stuff on screen 
     self.semaphore.signal() 
    } 
} 

buttonTappedDispatchSemaphore

extension DispatchSemaphore 
{ 
    var isBusy : Bool { return self.wait(timeout: .now()) == .timedOut } 
} 

에 편의를 사용하고 그리고 당신이 선호하는 경우이 논리를 반전 할 수 여기서 isIdle 그냥 것 self.wait(timeout: .now()) == .success

+0

이것은 생각이 떠오른다 - 나는 절대적으로 훌륭하다는 것을 의미한다. 그것은 아침 식사 전에 세 가지 놀라운 일들을 보는 것과 같습니다 :) – Fattie

+0

좋아, 한가지 이해하거나 따르기가 힘듭니다. 나는 세마포어의 수를 이해하려고 노력하고 있습니다. 1, 다음 performLongCalculation 0에서 다음에 증가 ... buttonTapped에서 -1로 감소거야? 그런 다음 waitForResult를 -2로 설정하고 wait ... – Fattie

+1

대기 시간이 초과되면 세마포어가 다시 증가합니다. –