我有一个应用程序需要尽快预加载一堆流式视频,以便在用户点击它们时立即播放。
我能够通过一组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
,仍然看到冻结
答案 0 :(得分:0)
您的dispatch_async代码不应该冻结主线程。那应该是在后台创建资产对象。资产可用之前需要一段时间,但这应该没问题。
你是什么意思“......应用程序仍然暂时冻结......”冻结怎么样?多久了?
加载后,如何使用_videoPlayers数组?你在做什么来处理数组可能只是部分加载的事实? (如果在从背景中保存到_videoPlayers数组时循环遍历,则可能会崩溃。)至少应该使videoPlayers成为您类的原子属性,并始终使用属性表示法引用它(读取和写入)({ {1}}或self.videoPlayers
,从不_videoPlayers。)您可能需要更好的保护,比如将@synchronized用于访问数组的代码。
答案 1 :(得分:0)
结果是后台线程正在锁定主线程正在其他地方访问的资源。主线程需要等待资源被释放,这导致接口冻结。