Async_Dispatch线程

时间:2015-09-14 00:21:40

标签: ios objective-c multithreading

我目前正在制作练习应用程序,并且正在使用GCD,并且在理解某些方面时遇到了一些麻烦。

我在这段代码中有一个问题:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    TWPhotoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];

    //cell.backgroundColor = [UIColor clearColor];

    dispatch_queue_t filterQueue = dispatch_queue_create("filter queue", NULL);

    dispatch_async(filterQueue, ^{
        UIImage *filterImage = [self filteredImageFromImage:self.photo.image usingFilter:[self.filters objectAtIndex:indexPath.row]];
        dispatch_async(dispatch_get_main_queue(), ^{
            cell.imageView.image = filterImage;
        });
    });

    return cell;
}

当你调用dispatch_async时,它基本上切换到我声明的队列,换句话说就是单独线程上的filterQueue?此外,我在第一个dispatch_async中调用dispatch_async返回主队列以进行一些UI更改。是否在我最初创建的自定义filterQueue的线程上调用了第二个dispatch_async(我切换到主队列的位置)?

2 个答案:

答案 0 :(得分:1)

首先,您需要移动在此方法之外创建调度队列的行。

您可能只应在程序生命周期内创建一次调度队列,或者至少在视图控制器的生命周期内创建一次。

您可以使filterQueue成为视图控制器的实例变量,然后将创建队列的代码移动到viewDidLoad方法。

然后你需要在视图控制器中添加一个dealloc方法,并在你的调度队列上调用dispatch_release。

现在关于您的问题

dispatch_async提交一个代码块以便在调度队列上进行处理。在这种情况下,您使用dispatch_queue_create()创建了目标队列。如果您阅读该函数的文档,他们会说如果为第二个参数传入NULL,则会得到一个串行队列。串行队列只能按FIFO顺序一次运行1个任务。

所以是的,您要求您的代码块在串行队列中运行,该队列在后台线程上运行。

该代码调用您的方法filteredImageFromImage,您需要确保该方法是线程安全的,并且不使用/设置self的任何实例变量。

完成对filteredImageFromImage的通话后,您可以拨打dispatch_async(dispatch_get_main_queue())。这说"从我的后台线程中,提交一个代码块以在主队列上运行(在主线程上运行。)"

您必须这样做,因为您无法从后台线程更改UI对象。 UIKit几乎没有线程安全。如果代码对视图进行了更改,则需要在主线程上完成。

考虑将dispatch_async与主队列以外的队列一起调用,要求助手为您做一些工作,同时继续做自己的事情。助理在后台线程上运行。

您要求助理在工作完成时通知您,以便您可以将其安装在视图中。这就是对dispatch_async(dispatch_get_main_queue())的调用。它有后台线程在主线程上调用代码。

请注意,您的代码存在编写的潜在问题

如果对filteredImageFromImage的调用仍在运行且用户在屏幕外滚动此单元格,则单元格将被回收以在集合视图中显示不同的条目。但是,当filteredImageFromImage完成时,生成的图像将被安装到" cell"中,它被回收并且现在在不同的indexPath上显示数据。

相反,您应该询问指定索引路径中单元格的集合视图,如果它仍然可用,那么您应该安装该图像:

dispatch_async(filterQueue, ^{
    UIImage *filterImage = [self filteredImageFromImage:self.photo.image usingFilter:[self.filters objectAtIndex:indexPath.row]];
    dispatch_async(dispatch_get_main_queue(), ^
    {
      //Ask the collection view for the target cell 
      UICollectionViewCell targetCell = [collectionView cellForItemAtIndexPath: indexPath];
      if (targetCell != nil)
        targetCell.imageView.image = filterImage;
    }
    );
});

编辑:

请注意,创建自己的队列并不常见。使用现有调度队列之一是更常见,更容易且通常更有效地使用系统资源。您可以使用函数`dispatch_get_global_queue()来获取现有队列。您可以使用dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT)或DISPATCH_QUEUE_PRIORITY_HIGH。但是,使用DISPATCH_QUEUE_PRIORITY_HIGH可能会导致设备响应性降低,因此应谨慎使用。

答案 1 :(得分:1)

Dispatch Queues是一种抽象类型,它脱离了线程模型。您对Dispatch Queues的所有了解都是他们可以派遣任务。这些任务与其他队列中的任务并行运行。

  

当你调用dispatch_async时,它基本上会切换到我声明的队列,换句话说就是单独一个线程上的filterQueue?

除了与filterQueue上发送的任务并行运行之外,dispatch_get_main_queue()上发送的任务不能得到任何保证。

  

我最初创建的自定义filterQueue的线程是否调用了第二个dispatch_async(我切换到主队列的位置)?

除了与dispatch_get_main_queue()上发送的任务并行运行之外,filterQueue上发送的任务不能得到任何保证。

现在有了所有的API保证,让我们进入当前的实现。

主队列上运行的所有任务都在一个线程中:主线程。创建filterQueue时,会为其分配一个线程池。任何这些线程都可以运行分派到filterQueue的任务。注意:此线程池将从不包含主线程。

现在两个问题的答案应该是显而易见的。在filterQueue上调度的任务在某个不是主线程的线程上运行。在dispatch_get_main_queue()上调度的任务在主线程上运行。