由后台线程引起的iOS界面冻结

时间:2016-02-05 01:12:31

标签: ios multithreading avfoundation

我有一个应用程序需要尽快预加载一堆流式视频,以便在用户点击它们时立即播放。

我能够通过一组AVPlayer个对象来实现这一点,并在应用程序启动时进行初始化:

-(void)preloadVideos {
    for (Video* video in arrayOfVideos){
        NSString *streamingURL = [NSString stringWithFormat:@"https://mywebsite.com/%@.m3u8", video.fileName];
        AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:streamingURL] options:nil];
        AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
        AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
        pthread_mutex_lock(&mutex_videoPlayers);
          [_videoPlayers setObject:player forKey:videoKey];
        pthread_mutex_unlock(&mutex_videoPlayers);
    }
}

锁在init中定义为:

pthread_mutex_init(&mutex_videoPlayers, NULL);

我的问题是,当我调用此功能时,应用会冻结大约1分钟,然后继续没有问题。这显然是因为有很多处理正在进行 - 根据xcode中的调试仪表板,在冻结期间CPU使用率激增至约67%。

所以我认为我可以通过将操作放入后台线程来解决这个问题:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    [self preloadVideos];
});

但应用程序仍然以完全相同的方式暂时冻结,并且CPU使用情况具有相同的模式。我想也许是因为任务过于密集,需要分解成较小的任务,所以我尝试将循环序列化为不同的任务:

preloadQueue = dispatch_queue_create("preloadQueue", NULL);
... 
-(void)preloadVideos {
    for (Video* video in arrayOfVideos){
        dispatch_async(preloadQueue, ^(void){
            [self preloadVideo:video]; // a new function with the logic above
        });
    }

但这似乎会使冻结时间更长,即使最大CPU使用率下降到48%。

我是否遗漏了这些GCD功能?为什么AVPlayer创建在放入后台任务时会阻塞主线程?

我知道并不是因为创建的AVPlayers太多了,因为它们只有6个,并且应用程序在创建后运行良好。

添加日志消息后,我注意到(在所有实现中),在调用接口的setObject方法之前,为每个视频播放器调用viewDidAppear调用。此外,5个视频立即加载,最后一个 - 较长的一个 - 需要一段时间,但冻结在完成时就结束了。

为什么应用程序在更新视图之前等待后台任务完成?

更新

当这些任务正在运行时,应用会访问videoPlayers,但由于我在写作时使用锁定,因此我在阅读时不会锁定。这是定义:

@property (atomic, retain) NSMutableDictionary *videoPlayers;

更新:使用互斥锁更新preloadVideos,仍然看到冻结

2 个答案:

答案 0 :(得分:0)

您的dispatch_async代码不应该冻结主线程。那应该是在后台创建资产对象。资产可用之前需要一段时间,但这应该没问题。

你是什么意思“......应用程序仍然暂时冻结......”冻结怎么样?多久了?

加载后,如何使用_videoPlayers数组?你在做什么来处理数组可能只是部分加载的事实? (如果在从背景中保存到_videoPlayers数组时循环遍历,则可能会崩溃。)至少应该使videoPlayers成为您类的原子属性,并始终使用属性表示法引用它(读取和写入)({ {1}}或self.videoPlayers,从不_videoPlayers。)您可能需要更好的保护,比如将@synchronized用于访问数组的代码。

答案 1 :(得分:0)

结果是后台线程正在锁定主线程正在其他地方访问的资源。主线程需要等待资源被释放,这导致接口冻结。