iOS中的GCD始终创建新线程

时间:2018-03-13 03:53:18

标签: ios objective-c grand-central-dispatch

我有很多这样的代码:

  

dispatch_async(dispatch_get_global_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 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:3)

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

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

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

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

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

在修订后的问题中,您会向我们展示一个代码段。所以,有几点想法:

  1. 不要担心[NSThread currentThread]。 GCD将为您管理线程。

  2. 这种缝合过程是否缓慢且可能使用相当程度的记忆?

    如果是这样,我不会建议一个串行队列(一次只允许一个串行队列),也不建议一个全局队列(因为你可能有足够的这些同时运行,你已经用完所有可用的工作线程),也不是GCD并发队列(同样,并发程度是未绑定的),而是使用具有一定合理有限并发度的NSOperationQueue

    @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);
                }
            }];
        }];
    }
    
  3. 如果您更喜欢使用自定义GCD串行队列(一次只能进行一次拼接操作)或自定义GCD并发队列(对任何给定时间运行的拼接任务数量没有限制),请随意。您知道这些操作的耗时和/或资源密集程度,因此只有您可以进行该调用。但是操作队列提供了并发的好处,但是简单地控制了并发度。

答案 2 :(得分:0)

并发队列的默认实现确实重用了线程但不等待空闲线程:如果没有空闲,它就会创建。

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

你需要 a)你自己的队列 b)'限制器',以便你

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