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

applicationWillTerminate死锁:应用终止时如何等待VZVirtualMachine停止?

解决VZVirtualMachine终止时的主线程阻塞问题

你的代码之所以卡壳,是因为semaphore.wait()直接阻塞了主线程,但stop的回调又被系统派发到主线程执行,形成了死锁:主线程等着回调发信号,回调却卡在队列里等主线程空闲。下面给你两种靠谱的解决思路:

方案一:后台线程处理stop(如果API允许)

如果VZVirtualMachine的stop方法支持在后台线程调用,那可以把stop操作放到后台队列,用DispatchGroup跟踪任务,在后台队列等待完成,完全不阻塞主线程:

func applicationWillTerminate(_ notification: Notification) {
    let runningEntities = runningEntities
    guard !runningEntities.isEmpty else { return }
    
    let stopGroup = DispatchGroup()
    let backgroundQueue = DispatchQueue(label: "com.yourapp.vm.stop.queue")
    
    runningEntities.forEach { entity in
        stopGroup.enter()
        backgroundQueue.async {
            entity.virtualMachine.stop { _ in
                stopGroup.leave()
            }
        }
    }
    
    // 在后台队列等待所有stop完成,不影响主线程
    stopGroup.wait()
}

方案二:主线程用RunLoop等待(必须在主线程调用stop时用)

如果stop必须在主线程调用,那就用DispatchGroup标记任务,然后通过RunLoop让主线程在等待时仍能处理回调事件,避免死锁:

func applicationWillTerminate(_ notification: Notification) {
    let runningEntities = runningEntities
    guard !runningEntities.isEmpty else { return }
    
    let stopGroup = DispatchGroup()
    var allVMsStopped = false
    
    runningEntities.forEach { entity in
        stopGroup.enter()
        entity.virtualMachine.stop { _ in
            stopGroup.leave()
        }
    }
    
    // 所有任务完成后在主线程标记状态
    stopGroup.notify(queue: .main) {
        allVMsStopped = true
    }
    
    // 循环让RunLoop处理事件,直到所有虚拟机停止
    while !allVMsStopped {
        RunLoop.current.run(until: Date().addingTimeInterval(0.1))
    }
}

原代码问题解析

原代码里DispatchSemaphore(value: -runningEntities.count)的逻辑本身没问题,但错在在主线程调用semaphore.wait()——主线程被完全阻塞后,stop的回调根本没机会执行,自然无法触发signal(),最终导致应用卡死无法正常终止。

内容的提问来源于stack exchange,提问作者Damian Dudycz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 04:17:27