2016-01-15 4 views
9

私は、一連の非同期イベントを起動し、その結果をバッファに書き込むアプリケーションを持っています。問題は、私は同期(非同期プロセスをスポーンスレッドで)Swift 2 - iOS - 元のスレッドにディスパッチ

スケルトンコードに書き込まれるバッファは、

let Session = NSURLSession.sharedSession() 
let TheStack = [Structure]() 
//This gets called asynchronously, e.g. in threads 3,4,5,6,7 
func AddToStack(The Response) -> Void { 
    TheStack.insertAt(Structure(The Response), atIndex: 0)) 
    if output.hasSpaceAvailable == true { 
     // This causes the stream event to be fired on mutliple threads 
     // This is what I want to call back into the original thread, e.g. in thread 2 
     self.stream(self.output, handleEvent: NSStreamEvent.hasSpaceAvailable) 
    } 
} 

// This is in the main loop, e.g. thread 2 
func stream(aStream: NSStream, handleEvent: NSStreamEvent) { 

    switch(NSStreamEvent) { 

     case NSStreamEvent.OpenCompleted: 
      // Do some open stuff 
     case NSStreamEvent.HasBytesAvailable: 
      Session.dataTaskWithRequest(requestFromInput, completionHandler: AddToStack) 
     case NSStreamEvent.HasSpaceAvailable: 
      // Do stuff with the output 
     case NSStreamEvent.CloseCompleted: 
      // Close the stuff 
    } 
} 

として問題が呼び出すスレッドであるたいことであるがdataTaskWithRequestになっています完了ハンドラは多くの異なるスレッドで起動し、スレッド3とスレッドが存在するすべてのスレッドで、case NSStreamEvent.HasSpaceAvailable:が実行されます。

私の質問は:self.stream(self.output, handleEvent: NSStreamEvent.hasSpaceAvailable)がスレッド3で呼び出された、またはそれまでの何らかの元のスレッドwこの出力フェーズでの相互のトリップを防止する。

ありがとうございます!

注:入力/出力処理が含まれているスレッドが、私はコメントから、私が何を私もともと行う方法を考え出した質問への助けを借りて、好奇心旺盛な傍観者のために、NSThread.detachNewThreadSelector

+2

独自のキューを作成し、使用する必要があるキューをライターに渡すのはどうですか? (あなたはgcdでタグ付けされているので私は「キュー」と言っています) –

+1

私は仕事を辞さなければならない約5分前にそれについて考えました。スレッド/キューを作成し、すべての出力をそれに集めます。そのようにしても、複数のスレッドが呼び出されていても、すべてを一度に実行しようとするわけではありません。私は火曜日までになっているので、もしそれが私が行くルートなら、あなたはそれがどうなるかを知らせます – Ajwhiteway

+0

私は既にNSThread.detachNewThreadSelectorによって作成されたNSThreadを持っています。これは私がコールバックする必要があるスレッドです。実際にはディスパッチが問題を悪化させる恐れがあります(ストリームがすべて終了する前に終了します)。 – Ajwhiteway

答えて

6

よしで作成されました

解決策は、特定のスレッドでperformSelectorを使用することです(コードに若干のスコープがある)。

final class ArbitraryConnection { 

internal var streamThread: NSThread 

let Session = NSURLSession.sharedSession() 
let TheStack = [Structure]() 
//This gets called asynchronously, e.g. in threads 3,4,5,6,7 
func AddToStack(The Response) -> Void { 
    TheStack.insertAt(Structure(The Response), atIndex: 0)) 
    if output.hasSpaceAvailable == true { 
     // This causes the stream event to be fired on multiple threads 
     // This is what I want to call back into the original thread, e.g. in thread 2 

     // Old way 
     self.stream(self.output, handleEvent: NSStreamEvent.hasSpaceAvailable) 
     // New way, that works 
     if(streamThread != nil) { 
      self.performSelector(Selector("startoutput"), onThread: streamThread!, withObject: nil, waitUntilDone: false) 
     } 
    } 
} 

func open -> Bool { 
    // Some stuff 
    streamThread = NSThread.currentThread() 
} 


final internal func startoutput -> Void { 
    if(output.hasSpaceAvailable && outputIdle) { 
     self.stream(self.output, handleEvent: NSStreamEvent.HasSpaceAvailable) 
    } 
} 
// This is in the main loop, e.g. thread 2 
func stream(aStream: NSStream, handleEvent: NSStreamEvent) { 

    switch(NSStreamEvent) { 

     case NSStreamEvent.OpenCompleted: 
      // Do some open stuff 
     case NSStreamEvent.HasBytesAvailable: 
      Session.dataTaskWithRequest(requestFromInput, completionHandler: AddToStack) 
     case NSStreamEvent.HasSpaceAvailable: 
      // Do stuff with the output 
     case NSStreamEvent.CloseCompleted: 
      // Close the stuff 
    } 
} 
} 

セレクタでオブジェクトに対してperformSelectorを使用し、onThreadを使用して渡すスレッドを指定します。セレクタを実行する前と呼び出しを行う前に、出力に空き容量があることを確認します(自分でトリップしないようにしてください)。

+0

GCDをラップし、同じキューまたは異なるキューからのディスパッチに関する問題を解決するので、私は 'performSelector'を使うでしょう。興味深いのは 'waitUntilDone:false'です。これは通常、操作の正しい順序付けを保証するために「真」であることを好みます。 – SwiftArchitect

+0

ええ、私は 'performSelector'を使ってテストするプロジェクトをまだ使っていません。これらがどのように生成されるかによって、スレッドごとに1つしか存在しません( 'dataTaskWithRequest'がそれを処理します)。ですから、 'false'にすることで、データがバッファのキューに追加されたらスレッドをクリアすることができます。まだ大規模なプロジェクトに取り込まれたときにすべてが一緒に保持されていることを確認するためにいくつかのテストを行う必要があります。 – Ajwhiteway

+0

あなたは完全にコントロールされているようです! 'performSelector'と' performBlock'を使った私の経験は、ここではミリ秒を節約するのが大変残念ですが、堅牢性と予測性のために 'waitUntilDone'と' performBlockAndWait'に戻す必要があります。 – SwiftArchitect

1

これは上のスレッドではコメントしません私が隠すために得るもの)が、waitUntilDoneまたはperformBlockAndWaitを使用すると、あなたの現在のコードがUIにデッドロックをかける可能性があることに注意してください。

このルートを経由する場合は、mainThreadからこれを呼び出さず、新しいスレッドを生成するフォールバックケースがないことを絶対に確かめる必要があります。

+0

これは私のコードで明示的に処理していない良い点です。私のケースでは、 'dataTaskWithRequest'の補完ハンドラがメインのGUIスレッドの中に終わることはありません。いずれにしても良い点です。あなたは自分自身以外の唯一の人で、質問に答えるので、賞金の報酬をお楽しみください! – Ajwhiteway

関連する問題