2017-12-29 53 views
3

スクリーンSを想像してください。ユーザーはSに着いて、物を見てください。あなたはBを押すとボタンBがDispatchSemaphoreをこの「開始ビジー」投機処理シナリオで使用してください

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

ジョシュの下の答えは、SOネット全体の単一の最良の回答の1つでなければならないでしょうork。 – Fattie

答えて

1

セマフォの値が1になるように、一度に処理できるのは1つだけです。これを考えると、NSLockも簡単に使用できます。セマフォを使用する方法の概要は次のとおりです。

計算を開始すると、セマフォにwait()(無期限)が記録されます。あなたが示唆したように、View Controllerのライフサイクル固有の順序付けを利用して、実際にブロックされないことを知ることができます。あなたが終わったら、 `signal()。明らかに、これはバックグラウンドですべて実行され、メインスレッドはブロックされません。

ボタンタップを処理するときに、瞬時のタイムアウトでセマフォを「待機」してテストし、その結果として.timedOutが表示されたらスピナーを表示します。セマフォが利用可能かタイムアウトかは、実際の待ち時間がないため、メインスレッドでこれを行うことができます。ここで信号が必要ないことに注意してください。のタイムアウトがの場合、セマフォは自動的に再インクリメントされます。待機が成功すると、ジョブが完了します。結果を直接表示します。

ジョブが完了していないと、スピナーが表示されます。今すぐ(無限に)バックグラウンドで待ってください。セマフォが通知されたため、その待機が終了すると、メインスレッドに戻り、スピナーを閉じて結果を提示します。

この時点で、セマフォのカウントは0です。したがって、このコードパスを再度通過する必要がある場合は、セマフォを通知する必要があります。基本的には

、あなたが作ったコードスケッチ記入:isIdleをちょうど次のようになります。buttonTappedDispatchSemaphore

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

上の利便性を使用しているそして、あなたが好む場合は、このロジックを逆転できること

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

self.wait(timeout: .now()) == .success

+0

これは心配です - 私は絶対に驚くべきことを意味します。それは朝食の前に3つの驚くべきことを見ているのと同じです:) – Fattie

+0

OK、私は理解したり従うのに苦労しています。私はセマフォの数を理解しようとしています。それは1で、次にperformLongCalculation 0で、その後増分されます。buttonTappedでは、それを-1に減らしますか?次にwaitForResultを-2にしてwait ... – Fattie

+1

待ち時間が切れると、セマフォが再び増分されます。 –

関連する問題