2016-07-05 11 views
2

私はいくつかのユーザー入力が必要なバックグラウンドスレッドを持っています。バックグラウンドスレッドからNSAlertウィンドウを呼び出すことはお勧めできませんので、私はメインスレッドでこれを行うのが好きです。しかし、NSAlertウィンドウが閉じられるまでバックグラウンドスレッドを待たせることはできますか?Swift:バックグラウンドスレッドがユーザー入力を待つ方法

let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT 
dispatch_async(dispatch_get_global_queue(priority, 0)) { 
    // some background task 

    dispatch_async(dispatch_get_main_queue()) { 
     // UI task asking for user input: 
     let alert = NSAlert() 
     alert.messageText = "Some text" 
     alert.informativeText = "Some information" 
     alert.addButtonWithTitle("Yes") 
     alert.addButtonWithTitle("No") 
     result = alert.runModal() 
    } 

    // some background task, treating user input (Yes/No) 
} 
+5

あなたは決してココアプログラミングで「待つ」べきではありません。 – matt

+0

作業を2つのセクションに分けることができますか?ユーザー入力前とユーザー入力後(別のメソッド、ブロックなど)?その後、ユーザー入力を取得した後、必要なデータをバックグラウンドキューに送信して、「ユーザー入力後」セクションを開始しますか?そうすれば、待ち行列をブロックする必要はありません。 – dylansturg

+1

マットの言うこととまったく同じです。あなたはそうしたようにタスクをスピンオフし、タスクが終了したときに実行されるコールバックを供給します。 – gnasher729

答えて

3

runModalは同期方式です。あなたはそれがかなりdispatch_asyncでメインキューに非同期に派遣よりも、完了するまで待機したい場合は、最も簡単にあなたが尋ねたものを達成できるためdispatch_sync、例えばと同期それを派遣することにより:

let queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0) 
dispatch_async(queue) { 
    // some background task 

    var result: NSModalResponse! 

    dispatch_sync(dispatch_get_main_queue()) {  // note, `dispatch_sync` 
     // UI task asking for user input: 
     let alert = NSAlert() 
     ... 
     result = alert.runModal() 
    } 

    // some background task, treating user input (Yes/No) 
} 

Aより微妙な問題は、同期性質runModalにあります。これにより、メインスレッドがブロックされます。これは一般的には良い考えではありません。メインスレッドが必要な他のタスク(タイマー、アニメーション、メインキューを使用するネットワーク操作など)に干渉する可能性があります。

ので、代わりに同期runModalの、あなたが例えば、バックグラウンドキューに追加バックグラウンド処理をディスパッチ完了ハンドラで、beginSheetModalForWindowと非同期NSAlertを提示し検討するかもしれない:

let queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0) 
dispatch_async(queue) { 
    // some background task 

    dispatch_async(dispatch_get_main_queue()) { // note, `dispatch_async` is OK here 
     // UI task asking for user input: 
     let alert = NSAlert() 
     ... 
     alert.beginSheetModalForWindow(NSApp.mainWindow!) { result in 
      dispatch_async(queue) { 
       // some background task, treating user input (Yes/No) 
      } 
     } 
    } 
} 

これは、あなたが望むものを達成し(モーダルが解除されたときに追加のバックグラウンドタスクを引き起こす)が、メインスレッドを全くブロックしないという利点があります。

-2

セマフォを使用できます。

dispatch_async(dispatch_get_global_queue(priority, 0)) { 
    // some background task 

    //Create a semaphore with count = 0 
    let semaphore = dispatch_semaphore_create(0) 

    dispatch_async(dispatch_get_main_queue()) { 
     // UI task asking for user input: 
     let alert = NSAlert() 
     alert.messageText = "Some text" 
     alert.informativeText = "Some information" 
     alert.addButtonWithTitle("Yes") 
     alert.addButtonWithTitle("No") 
     result = alert.runModal() 
     //Signal semaphore 
     dispatch_semaphore_signal(semaphore) 
    } 

    //Wait for semaphore 
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 
    // some background task, treating user input (Yes/No) 
} 

あなたは、メインスレッドでを "待つ" べきではありません。しかし、バックグラウンドスレッドは他のものです。

+1

はい、バックグラウンドスレッドで待つことは、メインスレッドで待ち続けるのと同じくらい深刻な問題ではありませんが、リソースの非効率的な使用であるため、やむを得ないことです。この場合、グローバルキューで使用できるワーカースレッドの数が限られています。 – Rob

+0

@Robはい、既にワーカースレッドの制限をほとんど使用している場合は、問題が発生する可能性があります。ちなみに、OS X/macOSの制限数はいくつですか?スレッドを起動するコストが無視できない場合は、頻繁にスレッドを呼び出すことを避ける必要があります。ケースバイケース。 「常に待ってはいけない」と言ってはいけませんが、「このケースが待ち受けに合っているかどうか常に確認してください」と言ってはいけません。 – OOPer

+1

最初にスレッドを起動するにはいくらかのコストがかかりますが、これはGCDを使用する理由の1つで、スレッドをプールするのではなく、スレッドのプールを利用するためです。第二に、私はあなたが「決して待たない」と断言すべきではないことに同意しますが、絶対に必要なときにのみ行うべきです(ここでは必要ない)。第3に、あなたは実際に( 'runModal'を呼び出すことによって)メインスレッドをブロックしてコードを悪化させました。ここで、ワーカースレッドのメインスレッド_and_ 1をブロックします。 – Rob

関連する問題