2017-02-05 4 views
1

Выполняю URLSession.shared.downloadTask запрос, но хотел бы выполнить код в том же потоке, на котором был выполнен downloadTask. Например:Как выполнить блок на экземпляре потока?

func sample() { 
    let thread = Thread.current 

    URLSession.shared.downloadTask(with: file) { 
     someFunc() //How to execute on thread variable? 
    }.resume() 
} 

В обработчик завершения downloadTask, он работает в фоновом потоке. Тем не менее, я бы хотел позвонить someFunc() в ту же самую нить sample(). Как мне что-то вроде Thread.current.async {...}, так что я могу это сделать:

func sample() { 
    let thread = Thread.current 

    URLSession.shared.downloadTask(with: file) { 
     thread.async { someFunc() } //Doesn't compile 
    }.resume() 
} 

ответ

1

Если вы хотите, чтобы запустить что-то на определенном Thread, вы бы не использовать этот НОД API, а просто:

perform(#selector(someMethod), on: thread, with: nil, waitUntilDone: false, modes: [RunLoopMode.commonModes.rawValue]) 

Это, конечно, предполагает, что вы создали нить с петлей выполнения, например:

let thread = Thread(target: self, selector: #selector(threadEntryPoint), object: nil) 
thread.start() 

и

func threadEntryPoint() { 
    autoreleasepool { 
     Thread.current.name = "com.domain.app.background" 
     let runLoop = RunLoop.current 
     runLoop.add(NSMachPort(), forMode: .defaultRunLoopMode) 
     runLoop.run() 
    } 
} 

Для получения дополнительной информации см. Threading Programming Guide.

Лично я лично остался в GCD, если бы был по-человечески, но вы сказали в другом месте, что у вас есть уникальное требование, которое исключает это.

+0

Это может решить проблему, но потоки очень низкоуровневые и сложные. Вы должны изменить вызов метода 'sample', который должен быть передан этому конкретному потоку через' выполнить on: '. Возможно, вам придется иметь дело с событиями вне порядка, создаваемыми другими потоками между 'sample' и' someFunc' ... То же самое можно архивировать с использованием высокоуровневых API, таких как 'OperationQueue', которые должны быть предпочтительным способом. – andih

+0

Я согласен, поэтому я закрыл «использовать GCD, если можно». Это, однако, не является хорошим вариантом использования для очередей операций. И блокирование вызывающего потока, как и в вашем ответе, никогда не является хорошей идеей. – Rob

+0

Вы правы, следует избегать блокирующего подхода. Другой подход, который позволяет избежать блокировки, заключается в использовании зависимых операций. – andih

0

Нет ничего похожего на Thread.current.async, поэтому он не компилируется.

Если вы хотите запустить sample и someFunc в том же потоке можно использовать Operation и OperationQueue. Недостатком такого подхода является то, что вы блокируете один поток.

var myQueue:OperationQueue = { 
    let q = OperationQueue() 
    q.name = "downloadQueue" 
    // if you want to limit concurrent operation 
    q.maxConcurrentOperationCount = 5 
    return q 
}() 


class DowloadOperation :Operation { 

    enum State { 
     case Ready, Executing, Finished 
     // ... 
    } 

    var state = State.Ready { 
     willSet { 
      ... 
     } 
     didSet { 
      ... 
     } 
    }  

    override func start() { 
     if self.isCancelled { 
      state = .Finished 
     } else { 
      state = .Ready 
      main() 
     } 
    } 

    override var isFinished: Bool { 
     get { 
      return state == .Finished 
     } 
    } 

    override var isAsynchronous: Bool { 
     get { 
      return true 
     } 
    } 

    override var isExecuting: Bool { 
     get { 
      return state == .Executing 
     } 
    } 


    override func main() { 
     if self.isCancelled { 
      self.state == .Finished 
      return 
     } 

     state = .Executing 

     URLSession.shared.downloadTask(with: file) { 
      // ... 
      state = .Finished 
     } 

     }.resume() 
    } 
} 

func someFunc() { 
    ... 
} 

// sample and someFunc are executed on the same thread 
// you loose the advantage of the async download. 
func sample() { 
    downloadOp = DownloadOperation() 
    myQueue.addOperation(downloadOp) 
    downloadOp.waitUntilFinished() 
    someFunc() 
} 
+0

Излишне говорить, что если вы выполняете асинхронный подкласс «Операция» (который является отличным шаблоном), читатели должны учитывать, что они должны делать необходимые уведомления о значении ключа. Я уверен, что вы намеревались сделать это, когда у вас есть ваши эллипсы в 'willSet' и' didSet', но это важная деталь или другие зависимости и т. Д., Просто не сработает. – Rob

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