2016-06-19 18 views
1

私はいくつかのセマフォの問題で並行処理プログラミングに入ります。 私の関数は、まずサーバーからデータをロードし、受信した情報を分析し、必要に応じてサーバーに2回目のリクエストを行います。iOSのURLリクエスト。セマフォの問題

私はそれを走らせるためにさまざまな方法を試しましたが、どれもうまくいきませんでした。 ME FOR私の現在のコードは正しいように見えますが、(多分デッドロックのような)は、第2のリクエストに応じてそれだけでロックし、最後のログがある「< __NSCFLocalDataTask:0x7ff470c58c90> {taskIdentifier:2} {一時停止}」

してください、何がわからないのか教えてください。おそらく、これらの目的のために完成品で作業するよりエレガントな方法がありますか?

ありがとうございます!

var users = [Int]() 
let linkURL = URL.init(string: "https://bla bla") 
let session = URLSession.shared() 
let semaphore = DispatchSemaphore.init(value: 0) 
let dataRequest = session.dataTask(with:linkURL!) { (data, response, error) in 
    let json = JSON (data: data!) 
    if (json["queue"]["numbers"].intValue>999) { 
     for i in 0...999 { 
      users.append(json["queue"]["values"][i].intValue) 
     } 
     for i in 1...lround(json["queue"]["numbers"].doubleValue/1000) { 
      let session2 = URLSession.shared() 
      let semaphore2 = DispatchSemaphore.init(value: 0) 
      let linkURL = URL.init(string: "https://bla bla") 
      let dataRequest2 = session2.dataTask(with:linkURL!) { (data, response, error) in 
       let json = JSON (data: data!) 
       print(i) 
       semaphore2.signal() 
      } 
      dataRequest2.resume() 
      semaphore2.wait(timeout: DispatchTime.distantFuture) 
     } 
    } 
    semaphore.signal() 
} 
dataRequest.resume() 
semaphore.wait(timeout: DispatchTime.distantFuture) 

P.S.なぜ私はそれをするのですか?サーバーは、限られた数のデータを返します。より多くを得るために、私はオフセットを使用する必要があります。

+0

なぜセマフォが必要ですか? 基本的には、最初の呼び出しの終了時に2番目のサーバー要求を実行する必要があり、2番目の呼び出し(内側の呼び出し)は結果を別のクロージャ経由で送信する必要があります。あなたが必要としていると思うなら、私はあなたにコードを渡します。 – OhadM

+0

@OhadM私が正しく理解しているように、クロージャは別のスレッドで実行されるので、メインスレッドはクロージャのコードが完了するのを待たない。私の場合、それは実行を開始しません。遊び場は止まる。だから私はセマフォを使用しています。 –

+0

1.別のスレッドで実行するように指示すると、クロージャは別のスレッドで実行されます。したがって、メインスレッドでも実行できます。 2.なぜ遊び場を使用しますか?あなたはアプリケーション環境全体でコードをテストする必要があります – OhadM

答えて

2

URLSessiondelegateQueueのセマフォを待っているため、デッドロックが発生しています。デフォルトの委任キューはメインキューではありませんが、シリアルバックグラウンドキューです(OperationQueuemaxConcurrentOperationCount1)。したがって、セマフォをシグナリングするはずの同じシリアルキュー上のセマフォをコードが待機しています。

戦術的な修正は、セッションの完了ハンドラが実行されているのと同じシリアルキューでwaitを呼び出さないようにすることです。 2つの明白な修正があります

  1. は(そのdelegateQueueシリアルキューです)sharedセッションを使用ではなく、あなた自身のURLSessionをインスタンス化し、作成同時OperationQueueするそのdelegateQueueを指定しないでください:

    let queue = OperationQueue() 
    queue.name = "com.domain.app.networkqueue" 
    
    let configuration = URLSessionConfiguration.default() 
    let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: queue) 
    
  2. また、セマフォを含むコードを他のキューにディスパッチすることで解決できます。完全を期すために

    let mainRequest = session.dataTask(with: mainUrl) { data, response, error in 
        // ... 
        DispatchQueue.global(attributes: .qosUserInitiated).async { 
         let semaphore = DispatchSemaphore(value: 0) 
    
         for i in 1 ... n { 
          let childUrl = URL(string: "https://blabla/\(i)")! 
          let childRequest = session.dataTask(with: childUrl) { data, response, error in 
           // ... 
           semaphore.signal() 
          } 
          childRequest.resume() 
          _ = semaphore.wait(timeout: .distantFuture) 
         } 
        } 
    } 
    mainRequest.resume() 
    

あなたが材料性能のペナルティを払って終わるだろうので、私は、あなたはおそらくすべてで、これらの要求を発行するセマフォを使用すべきではないことに注意しましょう一連の連続したリクエストを発行します(スレッドをブロックしていますが、これは一般的には推奨されません)。

これを行うためのこのコードのリファクタリングはもう少し相当です。基本的には一連の同時リクエストを発行し、おそらくメモリの影響を最小限に抑えるために「データ」タスクではなく「ダウンロード」タスクを使用し、すべてのリクエストが完了したら、最後に必要に応じてOperation「完了」操作またはディスパッチグループ通知)。

+0

ありがとう、それは私のために働く – Bruno