代码之家  ›  专栏  ›  技术社区  ›  ChokWah

iOS中的GCD始终创建新线程

  •  2
  • ChokWah  · 技术社区  · 6 年前

    我有很多这样的代码:

    dispatch\u async(dispatch\u get\u global\u queue(0,0),^{});

    dispatch_async 将在我调用它一次时创建一个新线程。 当应用程序运行一段时间后,我得到了许多线程:50、60、70。这不好。 如何重用这些线程。喜欢 tableview.dequeueReusableCellWithIdentifier

    这是我的密码。下载后需要做一些图像拼接的事情,然后保存。

    - (void)sdImageWith:(NSString *)urlString saveIn:(NSString *)savePath completion:(completionSuccess)successCompletion failure:(completionFalse)failureCompletion {
    
        [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:urlString] options:SDWebImageDownloaderUseNSURLCache progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
    
            if (data.length <= 100 || error != nil) { failureCompletion(error); return;}
    
            dispatch_async(imageStitch, ^{
                NSLog(@"thread:%@", [NSThread currentThread]);
                [[DLStitchingWarper  shareSingleton] StitchingImage:data savePath:savePath];
                if ([[NSFileManager defaultManager] fileExistsAtPath:savePath]) {
                    successCompletion(savePath);
                }else {
                    NSError *error = [[NSError alloc] initWithDomain:@"x" xxxcode:404 userInfo:nil];
                    failureCompletion(error);
                }
            });
        }]; 
    }
    
    3 回复  |  直到 6 年前
        1
  •  6
  •   Rob    6 年前

    这个 dispatch_get_global_queue 不一定要创建新线程。它将从GCD为您管理的有限“工作”线程池中提取线程。运行完调度的任务后,它会将此线程返回到工作线程池。

    当您向GCD队列调度某些内容时,它将从此池中获取可用的工作线程。您无法保证从一次调用到下一次调用它使用哪一个。但您不必担心它是否是另一个线程,因为GCD正在管理这个线程池,以确保不会不必要地创建和销毁线程。这是我们使用GCD而不是自己做的主要原因之一 NSThread 编程。效率要高得多。

    你唯一需要担心的是你在应用程序中使用的并发程度,这样你就不会耗尽这个工作线程池(对可能在同一个工作线程池中使用的其他后台任务产生意外影响)。

    限制并发程度最严厉的方法是使用您自己创建的共享串行队列。这意味着一次只能在该串行队列上运行一个对象。(注意,即使在这种情况下,也不能保证每次都使用相同的线程;只能保证一次只使用一个后台工作线程。)

    限制应用程序中并发程度的一种稍微更精确的方法是使用 NSOperationQueue (GCD上方的一层)并设置其 maxConcurrentOperationCount 。这样,您可以将并发度限制为大于1的值,但仍然足够小,不会耗尽工作线程。E、 g.对于网络队列,指定 maxConcurrentOperationCount 4或5的。


    在修改后的问题中,您向我们展示了一段代码片段。因此,有几个想法:

    1. 别担心什么 [NSThread currentThread] 。GCD将为您管理线程。

    2. 这种缝合过程是否很慢,并且可能会占用相当多的内存?

      如果是这样,我不建议使用串行队列(一次只允许一个队列可能太过约束),也不建议使用全局队列(因为您可以有足够多的队列同时运行,从而耗尽所有可用的工作线程),也不建议使用GCD并发队列(同样,并发度是未绑定的),而是使用 操作队列 具有某种合理的有限并发度:

      @property (nonatomic, strong) NSOperationQueue *stitchQueue;
      

      self.stitchQueue = [[NSOperationQueue alloc] init];
      self.stitchQueue.name = @"com.domain.app.stitch";
      self.stitchQueue.maxConcurrentOperationCount = 4;
      

      - (void)sdImageWith:(NSString *)urlString saveIn:(NSString *)savePath completion:(completionSuccess)successCompletion failure:(completionFalse)failureCompletion {
      
          [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:urlString] options:SDWebImageDownloaderUseNSURLCache progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
              if (data.length <= 100 || error != nil) { failureCompletion(error); return;}
      
              [self.stitchQueue addOperationWithBlock:^{
                  // NSLog(@"thread:%@", [NSThread currentThread]); // stop worrying about `NSThread`
                  [[DLStitchingWarper  shareSingleton] StitchingImage:data savePath:savePath];
                  if ([[NSFileManager defaultManager] fileExistsAtPath:savePath]) {
                      successCompletion(savePath);
                  }else {
                      NSError *error = [[NSError alloc] initWithDomain:@"x" xxxcode:404 userInfo:nil];
                      failureCompletion(error);
                  }
              }];
          }];
      }
      

    如果您喜欢使用自定义GCD串行队列(一次只能进行一次缝合操作)或自定义GCD并发队列(不限制在任何给定时间运行多少缝合任务),请随时使用。您知道这些操作有多么耗时和/或资源密集,因此只有您可以拨打该电话。但操作队列提供了并发的好处,但可以简单地控制并发程度。

        2
  •  4
  •   rmaddy    6 年前

    而不是使用 dispatch_get_global_queue 使用 dispatch_queue_create 并保存对队列的引用。像任何其他实例变量一样重用它。

        3
  •  2
  •   Daij-Djan    6 年前

    并发队列的默认实现确实重用线程,但不等待空闲线程:如果没有空闲线程,则itll create。

    因此,它会过度使用,您需要确保自己不会产生太多长时间运行的任务。对于“真正的”线程池

    你需要 a) 您自己的队列 b) “限制器”使您

    有关示例,请参见: IOS thread pool