I have many codes like this:
dispatch_async(dispatch_get_global_queue(0, 0), ^{});
dispatch_async will create a new thread when I call it one time.
When app run a while, I got many threads: number 50, 60, 70. It's not good.
How to reuse those threads. Like tableview.dequeueReusableCellWithIdentifier
This is my code. It's need do some image stitching things after download, then save.
- (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);
}
});
}];
}
The dispatch_get_global_queue doesn't necessarily create new threads. It will pull threads from a limited pool of "worker" threads that GCD manages for you. When it's done running your dispatched task, it will return this thread back to the pool of worker threads.
When you dispatch something to a GCD queue, it will grab an available worker thread from this pool. You have no assurances as to which one it uses from one invocation to the next. But you simply don't need to worry about whether it's a different thread, as GCD is managing this pool of threads to ensure that threads are not created and destroyed unnecessarily. It's one of the main reasons we use GCD instead of doing our own NSThread programming. It's a lot more efficient.
The only thing you need to worry about is the degree of concurrency that you employ in your app so that you don't exhaust this pool of worker threads (having unintended impact on other background tasks that might be drawing on the same pool of worker threads).
The most draconian way of limiting the degree of concurrency is to employ a shared serial queue that you create yourself. That means that only one thing will run on that serial queue at a time. (Note, even in this situation you don't have assurances that it will use the same thread every time; only that you'll only be using one background worker thread at a time.)
A slightly more refined way to constrain the degree of concurrency in your app is to use NSOperationQueue (a layer above GCD) and set its maxConcurrentOperationCount. With this, you can constrain the degree of concurrency to something greater than 1, but still small enough to not exhaust the worker threads. E.g. for network queues, it's not unusual to specify a maxConcurrentOperationCount of 4 or 5.
In your revised question, you show us a code snippet. So, a couple of thoughts:
Don't worry about what [NSThread currentThread]. GCD will manage the threads for you.
Is this stitching process slow and potentially using a fair degree of memory?
If so, I would not suggest either a serial queue (only allowing one at a time might be too constraining), nor a global queue (because you could have enough of these running concurrently that you'd use up all the available worker threads), nor a GCD concurrent queue (again, the degree of concurrency is unbound), but instead use an NSOperationQueue with some reasonable limited degree of concurrency:
@property (nonatomic, strong) NSOperationQueue *stitchQueue;
And
self.stitchQueue = [[NSOperationQueue alloc] init];
self.stitchQueue.name = @"com.domain.app.stitch";
self.stitchQueue.maxConcurrentOperationCount = 4;
And
- (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);
}
}];
}];
}
If you prefer to use a custom GCD serial queue (with only one stitching operation possible at a time) or a custom GCD concurrent queue (with no limit as to how many stitching tasks running at any given time), feel free. You know how time consuming and/or resource intensive these operations are, so only you can make that call. But operation queues offer the benefits of concurrency, but simple control over the degree of concurrency.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With