如何通过并行处理优化6000个JSON文件的Codable解析速度?
并行解析JSON文件优化方案
针对6000个JSON文件的串行解析耗时问题,以下是几种实用的并行处理方案,可大幅缩短解析时间:
一、Swift Concurrency(Async/Await,iOS 15+/macOS 12+ 推荐)
利用Swift原生的并发框架,代码简洁且自动管理并发调度:
func parseAllDrugs() async throws -> [Drug] { let drugMonographURLs = [/* 你的URL数组 */] let decoder = JSONDecoder() // 复用解码器,减少对象创建开销 return try await withThrowingTaskGroup(of: Drug.self) { group in // 为每个URL添加并行解析任务 for url in drugMonographURLs { group.addTask { let data = try Data(contentsOf: url) return try decoder.decode(Drug.self, from: data) } } // 串行收集所有解析结果(自动保证线程安全) var drugs = [Drug]() for try await drug in group { drugs.append(drug) } return drugs } }
二、GCD并发队列方案
兼容iOS 15以下版本,手动控制并发流程:
func parseAllDrugs(completion: @escaping (Result<[Drug], Error>) -> Void) { let drugMonographURLs = [/* 你的URL数组 */] // 创建自定义并发队列,避免占用全局高优先级队列 let concurrentQueue = DispatchQueue(label: "com.your.drug.parser", attributes: .concurrent) let group = DispatchGroup() let syncQueue = DispatchQueue(label: "com.your.drug.sync") // 串行队列保证数组线程安全 var drugs = [Drug]() var parseError: Error? let decoder = JSONDecoder() for url in drugMonographURLs { group.enter() concurrentQueue.async { guard parseError == nil else { group.leave() return } do { let data = try Data(contentsOf: url) let drug = try decoder.decode(Drug.self, from: data) // 串行队列同步写入数组 syncQueue.sync { drugs.append(drug) } } catch { syncQueue.sync { parseError = parseError ?? error } } group.leave() } } // 所有任务完成后回调结果 group.notify(queue: .main) { if let error = parseError { completion(.failure(error)) } else { completion(.success(drugs)) } } }
三、OperationQueue方案
通过操作队列灵活控制并发数:
func parseAllDrugs(completion: @escaping (Result<[Drug], Error>) -> Void) { let drugMonographURLs = [/* 你的URL数组 */] let queue = OperationQueue() // 根据CPU核心数设置并发上限,避免磁盘IO过度竞争 queue.maxConcurrentOperationCount = ProcessInfo.processInfo.activeProcessorCount var drugs = [Drug]() let syncQueue = DispatchQueue(label: "com.your.drug.sync") var parseError: Error? let decoder = JSONDecoder() // 完成操作,等待所有解析任务结束后回调 let completionOp = BlockOperation { DispatchQueue.main.async { if let error = parseError { completion(.failure(error)) } else { completion(.success(drugs)) } } } for url in drugMonographURLs { let parseOp = BlockOperation { guard parseError == nil else { return } do { let data = try Data(contentsOf: url) let drug = try decoder.decode(Drug.self, from: data) syncQueue.sync { drugs.append(drug) } } catch { syncQueue.sync { parseError = parseError ?? error } } } completionOp.addDependency(parseOp) queue.addOperation(parseOp) } OperationQueue.main.addOperation(completionOp) }
关键注意事项
- 控制并发数:磁盘IO的并发上限并非越高越好,建议设置为CPU核心数或2-4倍,过多并发会导致磁盘频繁寻址反而降低效率,需根据实际测试调整。
- 错误处理:上述示例若单个文件解析失败会终止整个流程,若需忽略错误继续解析,可修改代码收集成功结果和错误列表。
- 线程安全:必须保证数组写入操作的线程安全,可通过串行队列、锁或Swift Concurrency的TaskGroup自动收集实现。
- 性能优化:复用
JSONDecoder实例减少对象创建开销;若文件过大,可考虑异步读取文件内容(如FileHandle的异步API)避免内存峰值过高。
内容的提问来源于stack exchange,提问作者user2523320
相关产品推荐
相关产品推荐

