I need to perform a lot of calculations every time a getter is called from my app. The data returned from the getter is constantly changing based on the environment, and it has to do a lot of calculations to compute what it should return. Therefore, I don't want the code in the getter running on the main thread. This is what I have so far:
#interface Calculator ()
#property (nonatomic, strong) dispatch_queue_t calculationThread;
#end
- (dispatch_queue_t)calculationThread {
if (!_calculationThread) {
_calculationThread = dispatch_queue_create("calculation_thread", NULL);
}
return _calculationThread;
}
- (NSArray *)calculation {
// perform calculation in calculationThread, which should not be on main thread and be asynchronous
return arrayContainingCalculations;
}
I basically want to know how to use GCD to replace the comment. I have tried using dispatch_queue_t and dispatch_group_notify, but I don't seem to be implementing it correctly.
I think using a callback is probably the simplest and most efficient solution to this problem.
It is simply impossible to use only a single getter to do an asynchronous calculation without blocking the thread it was called on, as you expect code called after it to continue executing while it does the calculation.
You just have to create a new method with a callback, for example:
-(void) doCalculation:(void(^)(NSArray* result))callback {
dispatch_async(self.calculationQueue, ^{
NSArray* result = self.calculation; // make sure this is doing a synchronous calculation. If it's asynchronous, you'll have to use a semaphore (or another callback!).
if (callback) {
dispatch_async(dispatch_get_main_queue(), ^{ // return to main thread
callback(result);
});
}
});
}
Then you can simply invoke it on your main thread like so:
[calculator doCalculation:^(NSArray* result) {
textView.text = [result[0] stringValue]; // update UI with new info.
}];
That way you can easily keep your resulting code in-line with the call to the method.
It's also worth noting that your calculationQueue's getter (I renamed it, as the word thread is misleading when you're working with queues) isn't thread-safe. I would advise you use a dispatch_once to make it thread-safe:
-(dispatch_queue_t) calculationQueue {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_calculationQueue = dispatch_queue_create("calculation_queue", DISPATCH_QUEUE_SERIAL);
});
return _calculationQueue;
}
You can use the following to put it on your queue asynchronously. The problem however is that the method is going to return immediately.
dispatch_async(your_queue, ^{
// Code to be executed on background thread
});
What you probably want is to have some kind of method calculateWithCompletion where the caller can define a block that you can invoke once the completion is finished.
As you said in your comment to Peter, you want to keep it so you can call self.calculation and get your logic executed and return the calculation synchronously.
However because you want to avoid locking the UI while this logic is executing, you would like it to execute on a background thread.
Therefore, all you should need to do is use dispatch_sync instead of dispatch_async inside of your calculate method.
What dispatch_sync does is it places a task (the block that contains your logic) onto a specified queue (probably should pick a global concurrent queue), which then executes your task on a thread the OS picks for you (not the main thread). dispatch_async does the same, Except that dispatch_async will continue execution immediately after dispatching your task onto a queue.
dispatch_sync on the other hand, will block execution in the current run loop until your tasks returns.
This will allow you to execute your expensive logic on a background thread, while still remaining synchronous so that you can continue using self.calculation
Related
Does Option #1 below run some sort of implied queue? It does not seem to run on the main queue because when I tried to update UI stuff there it complained until I moved to Option #3 so I’m assuming that blocks have their own queue or thread? Before it complained I was under the impression if I didn’t start a dispatch queue that things would just run like normal, which in my mind would be on the main queue. Here’s some example code to illustrate:
// UserViewController.h
#interface UserViewController : NSObject
#property(nonatomic, strong) Server *server;
#property(nonatomic, strong) User *user;
#end
// UserViewController.m - Controller that sets a block for use in another class
#implementation UserViewController
- (void)doSomething {
// I'd like to call other methods and set #properties from the controller and I've heard
// __weak is the correct keyword to use (rather than __block or __strong).
__weak UserViewController *weakController = self;
// Option #0 - Outside of block
weakController.user = [[RHZUser alloc] init];
server.callbackBlock = ^(NSURLResponse *response, NSData *data, NSError *error) {
// Option #1 - Outside of dispatch queues. Is this in some sort of default queue?
weakController.user = [[RHZUser alloc] init];
dispatch_queue_t backgroundQueue
= dispatch_queue_create("com.example.backgroundQueue", nil);
dispatch_async(backgroundQueue, ^{
// Option #2 - This is on the serial queue I created
weakController.user = [[RHZUser alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
// Option #3 - The main queue where all my UI is
weakController.user = [[RHZUser alloc] init];
} // dispatch_async
} // dispatch_async
}; // self.callbackBlock
}
#end
// Server.m - Class that uses the block defined in the controller
#implementation Server
- makeAServerCall {
[NSURLConnection sendAsynchronousRequest:
[NSMutableURLRequest requestWithURL:restServiceURL]
queue:[[NSOperationQueue alloc] init]
completionHandler:self.callbackBlock];
}
#end
A block is a piece of code that, when executed, runs on a particular queue. Setting a block on some object does not make it run, nor does it get attached to one particular queue.
In the case of option 1, you set a block property on an instance of Server. This does not mean it's run, all it does is make that code accessible to anyone that has access to that block. Because the name of the property is callbackBlock, I'll assume that the Server instance executes the block when it finishes something.
That's when the block gets tied to a queue. Server's implementation decides whether it runs on the main queue or not, and should document (probably in its .h) whether it does or not. If it's not documented, but I absolutely need it run on the main queue, I always play it safe and make sure it gets called on the main thread by wrapping it in a dispatch_async.
EDIT:
Assuming your Server implementation is the same as Server's, you create a new queue using alloc/init and pass that to NSURLConnection. From the NSURLConnection documentation:
queue
The operation queue to which the handler block is dispatched when the request completes or failed.
So, the behavior is indeed documented and if you want the handler to be called on the main queue, simply pass dispatch_get_main_queue.
You can just call a block. Same as a call using a function pointer; actually the same syntax as using a function pointer. That block will just run on the same thread as the caller.
dispatch_block_t myBlock = ^ { NSLog (#"This is a block!"); };
myBlock ();
prints "This is a block!" on the same thread that the code is running on. callbacks will run on whatever thread they have been called on. So your "Option 1" block is performed on whatever queue the caller of that block decided to perform it on. Usually this should be documented by the method using the callback. There are some methods using callbacks that allow you to pass in a queue, and they will dispatch the callback on that queue.
Blocks are just things that you can call like a function. They do not care about or do anything with threads and queues per se. Your question would be exactly the same if you had asked "Using Grand Central Dispatch on iOS, what queue (if any) do regular C functions run on if they are not in a dispatch queue?" or "Using Grand Central Dispatch on iOS, what queue (if any) do regular Objective-C methods run on if they are not in a dispatch queue?" The answer to that would be the answer to the title of this question.
So what's the answer to those questions? Well, a function (or a method, or a block) runs when you call it. It's that simple. So by definition it runs on whatever thread or queue you were in when you call it. So what thread then code in your block runs in depends on what thread the code that calls it is in. So how is the block called? The block is passed to +[NSURLConnection sendAsynchronousRequest:queue:completionHandler:];, and it is that code that somehow calls it. We don't have the code of that library, but its documentation says that the completion handler is executed in the NSOperationQueue passed in as the second argument. You pass a new NSOperationQueue object as the second argument.
An NSOperationQueue maintains an internal thread or dispatch queue on which the operations are run. You don't have access to and should not care about this internal queue; you just know that the operations are executed on something that is separate from the other queues and threads, so is definitely not the main thread.
Its possible that Server has incorrectly (in my opinion) implemented their call back blocks on a queue that is not the main queue. You can check if option #1 is definitely not on the main queue by checking [NSThread isMainThread];. You usually only change UIKit elements on the main thread (there are some exceptions - as always!).
Usually web service callback (or completion) blocks are sent back to the main thread as is the case for AFNetworking for example.
Recently, I had the need for a function that I could use to guarantee synchronous execution of a given block on a particular serial dispatch queue. There was the possibility that this shared function could be called from something already running on that queue, so I needed to check for this case in order to prevent a deadlock from a synchronous dispatch to the same queue.
I used code like the following to do this:
void runSynchronouslyOnVideoProcessingQueue(void (^block)(void))
{
dispatch_queue_t videoProcessingQueue = [GPUImageOpenGLESContext sharedOpenGLESQueue];
if (dispatch_get_current_queue() == videoProcessingQueue)
{
block();
}
else
{
dispatch_sync(videoProcessingQueue, block);
}
}
This function relies on the use of dispatch_get_current_queue() to determine the identity of the queue this function is running on and compares that against the target queue. If there's a match, it knows to just run the block inline without the dispatch to that queue, because the function is already running on it.
I've heard conflicting things about whether or not it was proper to use dispatch_get_current_queue() to do comparisons like this, and I see this wording in the headers:
Recommended for debugging and logging purposes only:
The code must not make any assumptions about the queue returned,
unless it is one of the global queues or a queue the code has itself
created. The code must not assume that synchronous execution onto a
queue is safe from deadlock if that queue is not the one returned by
dispatch_get_current_queue().
Additionally, in iOS 6.0 (but not yet for Mountain Lion), the GCD headers now mark this function as being deprecated.
It sounds like I should not be using this function in this manner, but I'm not sure what I should use in its place. For a function like the above that targeted the main queue, I could use [NSThread isMainThread], but how can I check if I'm running on one of my custom serial queues so that I can prevent a deadlock?
Assign whatever identifier you want using dispatch_queue_set_specific(). You can then check your identifier using dispatch_get_specific().
Remember that dispatch_get_specific() is nice because it'll start at the current queue, and then walk up the target queues if the key isn't set on the current one. This usually doesn't matter, but can be useful in some cases.
This is a very simple solution. It is not as performant as using dispatch_queue_set_specific and dispatch_get_specific manually – I don't have the metrics on that.
#import <libkern/OSAtomic.h>
BOOL dispatch_is_on_queue(dispatch_queue_t queue)
{
int key;
static int32_t incrementer;
CFNumberRef value = CFBridgingRetain(#(OSAtomicIncrement32(&incrementer)));
dispatch_queue_set_specific(queue, &key, value, nil);
BOOL result = dispatch_get_specific(&key) == value;
dispatch_queue_set_specific(queue, &key, nil, nil);
CFRelease(value);
return result;
}
I used to have an helper method (static) that ensures that a completion block is called on the correct queue.
+ (void)_deadlockCheckBlock:(void(^)(void))block caller:(dispatch_queue_t)caller {
NSParameterAssert(block);
NSParameterAssert(caller);
if (caller == dispatch_get_current_queue()) {
block();
}
else {
dispatch_async(caller, block);
}}
Now to overcome the deprecation of
dispatch_get_current_queue()
I've rewritten the method using the get_main_queue method.
+ (void)_deadlockCheckBlock:(void(^)(void))block caller:(dispatch_queue_t)caller {
NSParameterAssert(block);
NSParameterAssert(caller);
dispatch_sync(dispatch_get_main_queue(), ^{
//This will ensure to be on the main queue
if (caller == dispatch_get_main_queue()) {
block();
}
else {
dispatch_async(caller, block);
}
});}
Is there a better way to get the same behaviour without going on the main queue?
The problem with that (and the reason get_current_queue is deprecated) is that there can be many current queues. If you dispatch_sync from one queue to another, both are now "current" in the sense that dispatch_sync to either of them will deadlock. Same with dispatch_set_target_queue.
The reliable ways to avoid deadlock are to always use dispatch_async (or other async APIs; dispatch_group_notify for example), to avoid reentrant control flow, or to pass along enough additional information to decide properly (if using a queue as a lock you could have an 'already locked' flag that you pass along for example).
In general, code that requires recursive locks or things that simulate them (like using the current queue to decide whether to dispatch_sync) is code that may have some invariant (the one the lock is protecting) broken and is expected to work anyway, which is a somewhat scary notion.
I have a method that at times can be invoked throughout my code. Below is a very basic example, as the code processes images and files off of the iphone photo gallery and marks them already processed when done with the method.
#property (nonatomic, assign) dispatch_queue_t serialQueue;
....
-(void)processImages
{
dispatch_async(self.serialQueue, ^{
//block to process images
NSLog(#"In processImages");
....
NSLog(#"Done with processImages");
});
}
I would think that each time this method is called I would get the below output...
"In processImages"
"Done with processImages"
"In processImages"
"Done with processImages"
etc...
but I always get
"In processImages"
"In processImages"
"Done with processImages"
"Done with processImages"
etc...
I thought a serial queue would wait till the first block is done, then start. To me it seems it is starting the method, then it gets called again and starts up before the first call even finishes, creating duplicates of images that normally would not be processed due to the fact that if it really executed serially the method would know they were already processed. Maybe my understanding of serial queues is not concrete. Any input? Thank you.
EDIT:MORE Context below, this is what is going on in the block...Could this cause the issue???
#property (nonatomic, assign) dispatch_queue_t serialQueue;
....
-(void)processImages
{
dispatch_async(self.serialQueue, ^{
//library is a reference to ALAssetsLibrary object
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop)
{
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop)
{
....
//Process the photos here
}];
failureBlock:^(NSError *error) { NSLog(#"Error loading images from library");
}];
});
}
-(id)init
{
self = [super init];
if(self)
{
_serialQueue = dispatch_queue_create("com.image.queue",NULL);
}
return self;
}
this object is only created once, and as far as I can tell can never be created again based off my code...I will run tests to make sure though.
UPDATE 2: WHAT I THINK IS HAPPENING, please comment on this if you agree/disagree....
Obviously my main issue is that it seems this block of code is being executed concurrently, creating duplicate entries (importing the same photo twice) when it wouldn't normally do this if it was run serially. When a photo is processed a "dirty" bit is applied to it ensuring the next time the method is invoked it skips this image, but this is not happening and some images are processed twice. Could this be due to the fact I am enumerating the objects in a second queue using enumerategroupswithtypes: within that serialQueue?
call processImages
enumerateObjects
immediately return from enumerateObjects since it is async itself
end call to processImages
processImages is not really done though due to the fact that enumerategroups is probably still running but the queue might thing it is done since it reaches the end of the block before enumerategroups is finished working. This seems like a possibility to me?
Serial Queues ABSOLUTELY will perform serially. They are not guaranteed to perform on the same thread however.
Assuming you are using the same serial queue, the problems is that NSLog is NOT guaranteed to output results in the proper order when called near simultaneously from different threads.
here is an example:
SQ runs on thread X, sends "In processImages"
log prints "In proc"
SQ on thread X, sends "Done with processImages"
SQ runs on thread Y, sends "In processImages"
log prints "essImages\n"
After 5., NSLog doesn't necessarily know which to print, 3. or 4.
If you absolutely need time ordered logging, You need a dedicated queue for logging. In practice, I've had no problems with just using the main queue:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"whatever");
});
If all NSlog calls are the on the same queue, you shouldn't have this problem.
enumerateGroupsWithTypes:usingBlock:failureBlock: does its work asynchronously on another thread and calls the blocks passed in when it's done (on the main thread I think). Looking at it from another perspective, if it completed all the synchronously by the time the method call was complete, it could just return an enumerator object of the groups instead, for instance, for a simpler API.
From the documentation:
This method is asynchronous. When groups are enumerated, the user may be asked to confirm the application's access to the data; the method, though, returns immediately. You should perform whatever work you want with the assets in enumerationBlock.
I'm not sure why you're trying to accomplish by using the serial queue, but if you just want to prevent simultaneous access, then you could just add a variable somewhere that keeps track of whether we're currently enumerating or not and check that at first, if you don't have to worry about synchronization issues. (If you do, perhaps you should look into using a GCD group, but it's probably overkill for this situation.)
If the question is "Can serial queue perform tasks asynchronously?" then the answer is no.
If you think that it can, you should make sure that all tasks are really performing on the same queue. You can add the following line in the block and compare the output:
dispatch_async(self.serialQueue, ^{
NSLog(#"current queue:%p current thread:%#",dispatch_get_current_queue(),[NSThread currentThread]);
Make sure that you write NSLog in the block that performs on your queue and not in the enumerateGroupsWithTypes:usingBlock:failureBlock:
Also you can try to create your queue like this
dispatch_queue_create("label", DISPATCH_QUEUE_SERIAL);
but I don't think that will change anything
EDIT:
By the way, method
enumerateGroupsWithTypes:usingBlock:failureBlock:
is asynchronous, why do you call it on another queue?
UPDATE 2:
I can suggest something like this:
dispatch_async(queue, ^{
NSLog(#"queue");
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER, *pmutex = &mutex;
pthread_mutex_lock(pmutex);
ALAssetsLibraryGroupsEnumerationResultsBlock listGroupBlock = ^(ALAssetsGroup *group, BOOL *stop) {
NSLog(#"block");
if (group) {
[groups addObject:group];
} else {
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
dispatch_async(dispatch_get_current_queue(), ^{
pthread_mutex_unlock(pmutex);
});
}
NSLog(#"block end");
};
[assetsLibrary enumerateGroupsWithTypes:groupTypes usingBlock:listGroupBlock failureBlock:failureBlock];
pthread_mutex_lock(pmutex);
pthread_mutex_unlock(pmutex);
pthread_mutex_destroy(pmutex);
NSLog(#"queue end");
});
I hit an issue like this, and the answer for me was to realize that asynchronous calls from a method on the serialized queue goes to another queue for processing -- one that is not serialized.
So you have to wrap all the calls inside the main method with explicit dispatch_async(serializedQueue, ^{}) to ensure that everything is done in the correct order...
Using Swift and semaphores to illustrate an approach to serialization:
Given: a class with an asynchronous ‘run’ method that will be run on multiple objects at once, and the objective is that each not run until the one before it completes.
The issue is that the run method allocates a lot of memory and uses a lot of system resources that can cause memory pressure among other issues if too many are run at once.
So the idea is: if a serial queue is used then only one will run at a time, one after the other.
Create a serial queue in the global space by the class:
let serialGeneratorQueue: DispatchQueue = DispatchQueue(label: "com.limit-point.serialGeneratorQueue", autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem)
class Generator {
func run() {
asynchronous_method()
}
func start() {
serialGeneratorQueue.async {
self.run()
}
}
func completed() {
// to be called by the asynchronous_method() when done
}
}
The ‘run’ method of this class for which very many objects will be created and run will be processed on the serial queue:
serialGeneratorQueue.async {
self.run()
}
In this case an autoreleaseFrequency is .workItem to clean up memory after each run.
The run method is of some general form:
func run() {
asynchronous_method()
}
The problem with this: the run method exits before the asynchronous_method completes, and the next run method in the queue will run, etc. So the objective is not being achieved because each asynchronous_method is running in parallel, not serially after all.
Use a semaphore to fix. In the class declare
let running = DispatchSemaphore(value: 0)
Now the asynchronous_method completes it calls the ‘completed’ method:
func completed() {
// some cleanup work etc.
}
The semaphore can be used to serialized the chain of asynchronous_method’s by add ‘running.wait()’ to the ‘run’ method:
func run() {
asynchronous_method()
running.wait()
}
And then in the completed() method add ‘running.signal()’
func completed() {
// some cleanup work etc.
running.signal()
}
The running.wait() in ‘run’ will prevent it from exiting until signaled by the completed method using running.signal(), which in turn prevents the serial queue from starting the next run method in the queue. This way the chain of asynchronous methods will indeed be run serially.
So now the class is of the form:
class Generator {
let running = DispatchSemaphore(value: 0)
func run() {
asynchronous_method()
running.wait()
}
func start() {
serialGeneratorQueue.async {
self.run()
}
}
func completed() {
// to be called by the asynchronous_method() when done
running.signal()
}
}
I thought a serial queue would wait [until] the first block is done ...
It does. But your first block simply calls enumerateGroupsWithTypes and the documentation warns us that the method runs asynchronously:
This method is asynchronous. When groups are enumerated, the user may be asked to confirm the application's access to the data; the method, though, returns immediately.
(FWIW, whenever you see a method that has a block/closure parameter, that’s a red flag that the method is likely performing something asynchronously. You can always refer to the relevant method’s documentation and confirm, like we have here.)
So, bottom line, your queue is serial, but it is only sequentially launching a series of asynchronous tasks, but obviously not waiting for those asynchronous tasks to finish, defeating the intent of the serial queue.
So, if you really need to have each tasks wait for the prior asynchronous task, there are a number of traditional solutions to this problem:
Use recursive pattern. I.e., write a rendition of processImage that takes an array of images to process and:
check to see if there are any images to process;
process first image; and
when done (i.e. in the completion handler block), remove the first image from the array and then call processImage again.
Rather than dispatch queues, consider using operation queues. Then you can implement your task as an “asynchronous” NSOperation subclass. This is a very elegant way of wrapping an asynchronous task This is illustrated in https://stackoverflow.com/a/21205992/1271826.
You can use semaphores to make this asynchronous task behave synchronously. This is also illustrated in https://stackoverflow.com/a/21205992/1271826.
Option 1 is the simplest, option 2 is the most elegant, and option 3 is a fragile solution that should be avoided if you can.
You might have more than one object, each with its own serial queue. Tasks dispatched to any single serial queue are performed serially, but tasks dispatched to different serial queues will absolutely be interleaved.
Another simple bug would be to create not a serial queue, but a concurrent queue...
I have a GCD that goes on the background. I have a button that when pressed I want it to load a loading wait screen while the GCD finishes, and then execute the rest of the code on that button. Attached is the sample.
Mine does not work, I basically want to say, wait as long as it takes to finish GCD, and load a waiting message in the meantime, when done continue code.
Thank you
- (IBAction)btnTapped:(id)sender
{
shouldCancel=NO;
dispatch_queue_t existingQueque = dispatch_get_main_queue();//finds the current GCD, the one I created in a different method
dispatch_group_t group =dispatch_group_create();
dispatch_group_async(group, existingQueque, ^
{
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//does not work, I guess group can't be created here.
[self performSelectorOnMainThread:#selector(showWaitViewWithMessage:) withObject:#"Loading" waitUntilDone:YES];//load this until GCD queque done
[self performSelector:#selector(performSearch) withObject:nil afterDelay:0];
});
}
A couple of thoughts:
You suggest that dispatch_get_main_queue() "finds the current GCD, the one I created in a different method". No, this just gets the main queue (the one that, if you use it, will block your user interface), not the queue that you created elsewhere through dispatch_create_queue. The dispatch_get_main_queue() just gets the main queue, and while your searching is happening, your UI will be blocked (e.g. UIActivityIndicatorView won't spin, whatever).
If you've dispatched a whole bunch of tasks to a background queue, if you want to wait for all of them to finish, that's when you use dispatch_group_t or dispatch_barrier, but given what you've shown doesn't require that (you have only one dispatched operation), you just don't need to go there. By the way, barriers are not recommended if you're using global queues.
The typical pattern for a single GCD background task is more simple than your question suggests. You (a) update your UI to say "loading" and show a UIActivityIndicatorView or something like that, so the user has a richer UX showing them that the app is working on something; (b) dispatch the search in the background; and (c) when done, dispatch the UI update back to the main queue. Thus, the typical pattern is:
- (IBAction)btnTapped:(id)sender
{
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// or, if you've already created you own background queue just use that here,
// or just create one here. But don't use dispatch_get_main_queue, as that
// won't use a background queue.
//
// dispatch_queue_t backgroundQueue = dispatch_queue_create("org.yourdomain.yourapp.search", NULL);
[self showWaitViewWithMessage:#"Loading"];
dispatch_async(backgroundQueue, ^{
[self performSearch]; // do this in the background
dispatch_async(dispatch_get_main_queue(), ^{
[self updateUiAfterSearch]; // when done, dispatch UI update back to main queue
});
});
// if you created a queue, remember to release it
//
// dispatch_release(backgroundQueue);
}
As an aside, in your performSelectorOnMainThread, I see no reason to waitUntilDone. Don't wait unless there is some compelling reason to do so. As you see above, this construct isn't needed at all, but just a FYI.
By the way, it's important to know that many servers impose limits on how many concurrent requests a given client may make at a time. If it's possible that you might be initiating multiple requests (e.g. the user taps buttons and the server is slow to respond) and this allows them to run concurrently. In this scenario, it's worth pursuing NSOperationQueue, where you can set maxConcurrentOperationCount. If you use the block versions of the NSOperationQueue methods (e.g. addOperationWithBlock rather than GCD's dispatch_async), the code can be structured in the same way, but it let's you constrain the number of background operations.
Also, NSOperationQueue offers the ability to easily establish dependencies between the operations (e.g. a completion NSOperation that is dependent on all of the others finishing). I can outline that, but the code you posted doesn't necessitate that, so I'll spare you that unless you let me know you want to see what that would look like.
you have to save the queue you create, dont create it each time and if you only want one at a time, use a serial queue
#implementation DDAppDelegate {
dispatch_queue_t queue;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self do];
[self performSelector:#selector(do) withObject:nil afterDelay:1];
}
- (void)do {
if(!queue)
queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
//serialized
NSLog(#"1");
sleep(10);
});
}
#end
if you want a concurrent queue, use a global queue and dispatch_barrier_async
#implementation DDAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self do];
[self performSelector:#selector(do) withObject:nil afterDelay:1];
}
- (void)do {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_barrier_async(queue, ^{
//serialized
NSLog(#"1");
sleep(10);
});
}