You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建用于后台线程任务的串行Dispatch Group?

实现后台串行视频分段导出的正确姿势

嘿,我来帮你搞定这个视频分段导出的串行任务问题!其实核心思路就是用一个后台串行队列控制任务顺序,配合DispatchGroup跟踪每个导出任务的生命周期,确保前一个导出完成后再启动下一个。下面给你完整的实现方案和细节说明:

1. 核心组件梳理

首先我们需要几个关键部分:

  • 一个后台串行队列:保证同一时间只有一个导出任务在运行,避免CPU/磁盘资源过载(视频导出属于高负载任务)
  • DispatchGroup:用来精准跟踪每个导出任务的开始与结束,防止任务遗漏导致的流程中断
  • 递归处理逻辑:逐个启动分段导出,当前任务完成后自动触发下一个,完美贴合你要的串行执行需求

2. 完整代码实现

先上可直接复用的代码,后面再拆解细节:

import AVFoundation

// 定义视频分段模型,统一管理每个分段的参数
struct VideoSegment {
    let asset: AVAsset
    let timeRange: CMTimeRange
    let outputURL: URL
}

class VideoExporter {
    func exportSegmentsSequentially(segments: [VideoSegment]) {
        // 创建后台串行队列,设置background服务质量适配后台任务
        let exportQueue = DispatchQueue(label: "com.yourapp.video.export", qos: .background)
        let exportGroup = DispatchGroup()
        
        // 递归函数:处理第index个分段,完成后自动启动下一个
        func processSegment(at index: Int) {
            guard index < segments.count else {
                // 所有分段处理完成,切回主队列更新UI
                DispatchQueue.main.async {
                    print("✅ 所有视频分段导出完成!")
                    // 这里可以添加UI逻辑,比如弹出成功提示、更新进度条等
                }
                return
            }
            
            let currentSegment = segments[index]
            exportGroup.enter() // 标记启动一个新任务
            
            exportQueue.async {
                // 创建AVAssetExportSession实例
                guard let exportSession = AVAssetExportSession(
                    asset: currentSegment.asset,
                    presetName: AVAssetExportPresetHighestQuality
                ) else {
                    print("❌ 无法创建导出会话(分段\(index+1))")
                    exportGroup.leave() // 任务失败也要标记结束
                    processSegment(at: index + 1) // 继续下一个分段
                    return
                }
                
                // 配置导出参数
                exportSession.outputURL = currentSegment.outputURL
                exportSession.outputFileType = .mov // 可根据需求调整为.mp4等格式
                exportSession.timeRange = currentSegment.timeRange
                
                // 启动异步导出
                exportSession.exportAsynchronously {
                    defer { exportGroup.leave() } // 无论成功失败,确保标记任务结束
                    
                    // 处理导出结果
                    switch exportSession.status {
                    case .completed:
                        print("✅ 分段\(index+1)导出成功:\(currentSegment.outputURL.path)")
                        // 这里可以添加成功后的逻辑,比如保存路径到本地存储
                    case .failed:
                        let errorMsg = exportSession.error?.localizedDescription ?? "未知错误"
                        print("❌ 分段\(index+1)导出失败:\(errorMsg)")
                    case .cancelled:
                        print("⚠️ 分段\(index+1)导出被取消")
                    default:
                        break
                    }
                    
                    // 启动下一个分段的导出
                    processSegment(at: index + 1)
                }
            }
        }
        
        // 启动第一个分段的导出流程
        processSegment(at: 0)
    }
}

// 用法示例:
// let sourceAsset = AVAsset(url: yourSourceVideoURL)
// let segments = [
//     VideoSegment(asset: sourceAsset, timeRange: CMTimeRange(start: .zero, duration: CMTime(seconds: 10, preferredTimescale: 600)), outputURL: yourOutputURL1),
//     // 填充剩下4个分段的配置...
// ]
// let exporter = VideoExporter()
// exporter.exportSegmentsSequentially(segments: segments)

3. 关键细节解释

  • 串行队列的意义:DispatchQueue(label:..., qos: .background)是串行队列,添加到队列的任务会严格按顺序执行,不会同时运行多个导出任务,避免系统资源竞争导致的导出失败或卡顿。
  • DispatchGroup的正确用法:每个导出任务启动前调用enter(),结束后(无论成功失败)调用leave(),用defer确保leave()一定会被执行,防止Group一直处于等待状态。
  • 递归逻辑的优势:相比循环等待,递归的方式更优雅,每个任务完成后自动触发下一个,完全符合你要的“等待上一个完成再启动下一个”的需求。
  • 线程切换注意:AVAssetExportSession的回调是在后台队列的,所以如果要更新UI,必须切换到主队列(DispatchQueue.main.async)。

4. 额外注意事项

  • 输出路径校验:每个分段的outputURL必须唯一,且所在目录必须存在。可以提前创建目录:
    let outputDir = currentSegment.outputURL.deletingLastPathComponent()
    try? FileManager.default.createDirectory(at: outputDir, withIntermediateDirectories: true)
    
  • 导出预设选择:AVAssetExportPresetHighestQuality适合高质量导出,但速度较慢;如果追求导出速度,可以选择AVAssetExportPresetMediumQuality等预设,根据需求调整。
  • 取消功能扩展:如果需要支持取消导出,可以给VideoExporter添加isCancelled属性,在导出前检查状态,调用exportSession.cancel()终止任务。

内容的提问来源于stack exchange,提问作者Roi Mulia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:57:29