我有很多这样的代码:
当我调用它时,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);
}
});
}];
}
答案 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并不罕见。
在修订后的问题中,您会向我们展示一个代码段。所以,有几点想法:
不要担心[NSThread currentThread]
。 GCD将为您管理线程。
这种缝合过程是否缓慢且可能使用相当程度的记忆?
如果是这样,我不会建议一个串行队列(一次只允许一个串行队列),也不建议一个全局队列(因为你可能有足够的这些同时运行,你已经用完所有可用的工作线程),也不是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);
}
}];
}];
}
如果您更喜欢使用自定义GCD串行队列(一次只能进行一次拼接操作)或自定义GCD并发队列(对任何给定时间运行的拼接任务数量没有限制),请随意。您知道这些操作的耗时和/或资源密集程度,因此只有您可以进行该调用。但是操作队列提供了并发的好处,但是简单地控制了并发度。
答案 2 :(得分:0)
并发队列的默认实现确实重用了线程但不等待空闲线程:如果没有空闲,它就会创建。
因此它会过度使用,你需要确保自己不会产生太多长时间运行的任务。对于“真正的”线程池你需要 a)你自己的队列 b)'限制器',以便你
有关示例,请参阅:IOS thread pool