2016-01-15 3 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 в thread, скажем, 3. Обработчик завершения срабатывает во многих разных потоках и вызывает работу case NSStreamEvent.HasSpaceAvailable: в потоке 3, а также все потоки, в которых они существуют.

Мой вопрос: Как сделать так, чтобы self.stream(self.output, handleEvent: NSStreamEvent.hasSpaceAvailable) был вызывается в потоке 3 или какой-либо исходный поток w чтобы предотвратить это отключение друг от друга в выходной фазе.

Заранее благодарен!

Примечание: Поток, который содержит обработку ввода/вывод был создан с NSThread.detachNewThreadSelector

+2

Как насчет создания собственной очереди (ов) и передачи той, которая должна использоваться вашим автором? (Я говорю «очередь», потому что вы отметили gcd.) –

+1

Я думал об этом примерно за 5 минут до того, как мне пришлось покинуть работу. Создайте поток/очередь и переместите весь вывод на это. Таким образом, даже если есть несколько потоков, вызывающих, они не все пытаются выполнить сразу. Я уезжаю до вторника сейчас, так что если это маршрут, я пойду, я дам вам знать, как это происходит. – Ajwhiteway

+0

У меня уже есть NSThread, созданный NSThread.detachNewThreadSelector. Это поток, который мне нужно перезвонить. Я боюсь, что диспетчеризация может на самом деле ухудшить проблему (закрытие потоков до того, как все будет завершено) – Ajwhiteway

ответ

6

Хорошо, для любопытного зрителя I, с помощью от комментариев на вопрос, я понял, как сделать то, что я первоначально заданный в вопросе (независимо от того, переписывается ли это в конечном итоге для использования GCD - это другой вопрос)

Решение (с небольшим увеличением объема кода) заключается в использовании функции 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

Я бы придерживался 'performSelector', поскольку он обертывает GCD и решает загадки вокруг отправки из одинаковых или разных очередей. Интересно, что 'waitUntilDone: false', который я обычно предпочитаю быть« истинным », чтобы обеспечить правильное упорядочение операций. – SwiftArchitect

+0

Да, я завернул проект для тестирования с 'performSelector', который все еще используется. Из-за того, как они появляются, обычно существует только один поток (обработчик dataTaskWithRequest). Поэтому, делая это 'false', я разрешаю поток очищаться после того, как данные были добавлены в очередь для буфера. Все еще есть, чтобы пройти через некоторое тестирование, чтобы убедиться, что все держится вместе, когда помещается в более крупный проект. – Ajwhiteway

+0

Вы, кажется, полностью контролируете! Мой опыт работы с 'performSelector' и' performBlock' заключается в том, что я вообще сожалею о сохранении миллисекунд здесь и там и должен вернуться к 'waitUntilDone' и' performBlockAndWait' за надежность и предсказуемость. – SwiftArchitect

1

Это не позволит мне прокомментировать эту тему (это что я получаю для скрытия), но одна вещь, о которой нужно знать, заключается в том, что ваш текущий код может блокировать ваш пользовательский интерфейс, если вы используете waitUntilDone или performBlockAndWait.

Если вы идете по этому маршруту, вы должны быть абсолютно уверены, что вы не вызываете это из mainThread или имеете запасной корпус, который порождает новый поток.

+0

Это хороший момент, который я не обработал явно в моем коде, в моем случае обработчик завершения dataTaskWithRequest 'никогда не должен заканчиваться внутри основного потока графического интерфейса. В любом случае хороший момент. Поскольку вы были единственным человеком, кроме меня, чтобы ответить на вопрос, наслаждайтесь наградой за награду! – Ajwhiteway

Смежные вопросы