オペレーションキューとブロックオペレーションを使って次のようにするのはいかがでしょうか。
(list
の情報がないので、一旦Int
の配列で実装しています。)
BlockOperation
に渡した処理の中で、キャンセル状態を監視してキャンセルされていたら自らやめるようにすれば処理のキャンセルを実現できます。
swift
1 let list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2
3 private var operationQueue: OperationQueue?
4
5 /// 複数の処理を並列に実行する
6 func doMultiAsyncProcess(code: String) {
7 guard operationQueue == nil else { return }
8
9 operationQueue = OperationQueue()
10
11 let blockOperation = BlockOperation()
12 let currentQueue = operationQueue
13 blockOperation.completionBlock = {
14 // 全部完了したときの処理。キャンセルした場合も呼ばれる。
15 print("All process for (code) has been done.")
16 // キャンセルされて次の処理が始まっていたら違うキューに変わっている
17 if currentQueue == self.operationQueue {
18 self.operationQueue = nil
19 }
20 }
21
22 // 非同期処理をオペレーションに登録
23 list.forEach { (value) in
24 blockOperation.addExecutionBlock {
25 guard !blockOperation.isCancelled else { return }
26 Thread.sleep(forTimeInterval: Double.random(in: 1..<2))
27 guard !blockOperation.isCancelled else { return }
28 // API通信開始
29 self.getAPIResult(item:value, code: code, operation: blockOperation)
30 }
31 }
32
33 // 実行開始
34 operationQueue?.addOperation(blockOperation)
35 }
36
37 /// 実行中の処理をキャンセルする。
38 func cancellMultiAsyncProcess() {
39 guard let queue = operationQueue else { return }
40 print("Request cancell of the operations.")
41 queue.cancelAllOperations()
42 operationQueue = nil
43 }
44
45 /// サーバーと通信する。ダミーで単にウェイトして通信した事にする処理としています。また、キャンセルで中断するためにオペレーションも伝えています。
46 func getAPIResult(item: Int, code: String, operation: Operation) {
47 print("getAPIResult(item:(item), code:(code) is started.")
48 Thread.sleep(forTimeInterval: Double.random(in: 1..<5))
49 if operation.isCancelled {
50 print("getAPIResult(item:(item), code:(code) Cancelled.")
51 } else {
52 print("getAPIResult(item:(item), code:(code) received a result.")
53 }
54 }
多分こんな感じでいいと思いますが、問題あれば教えてください。
なお、元のソースでDispatchGroup
へのenter()
とleave()
の呼び出しは必要ありません。
追記
並列度合いを調整するには、オペレーションキューのmaxConcurrentOperationCount
を使うと簡単です。
この場合は、キューが処理数を制御できるように、ひとつひとつの処理をそれぞれ別のBlockOperation
に分割して登録することになります。また、こうすると全処理の完了検知にBlockOperation
のcompletion
を使うことができないので、かわりに、completionの処理を一つのBlockOperation
として、他の処理が終わったら実行するように設定してオペレーションキューに入れました。
このように変更したdoMultiAsyncProcess
は以下のようになると思います。
func doMultiAsyncProcess(code: String) {
guard operationQueue == nil else { return }
operationQueue = OperationQueue()
// 並行処理数を3つまでに制限。
operationQueue?.maxConcurrentOperationCount = 3
// 全部が完了したら実行する処理。(キャンセルした時は呼ばれない)
let currentQueue = operationQueue
let completion = BlockOperation()
completion.addExecutionBlock {
print("All process for (code) has been done.")
if currentQueue == self.operationQueue {
self.operationQueue = nil
}
}
// 非同期処理をオペレーションキューに登録
list.forEach { (value) in
var blockOperation = BlockOperation()
blockOperation.addExecutionBlock {
guard !blockOperation.isCancelled else { return }
Thread.sleep(forTimeInterval: Double.random(in: 1..<2))
guard !blockOperation.isCancelled else { return }
// API通信開始
self.getAPIResult(item:value, code: code, operation: blockOperation)
}
completion.addDependency(blockOperation)
operationQueue?.addOperation(blockOperation)
}
operationQueue?.addOperation(completion)
}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/12/25 03:46 編集
2019/12/24 08:48
2019/12/25 03:49
2019/12/26 04:18
2019/12/26 06:38 編集
2019/12/27 00:59