I have a refresh button on my iOS application that launches an asynchronous dispatch queue in GCD. The name of the queue is custom. There can be issues where the user bangs the heck out of the button and causes a large amount of unnecessary queues to be created. My hope is to check to see if there is a queue with a specific name active so I could not launch another queue or add to the same queue of the same name.
Is this possible?
I don't think you should create new queues on every request. And since you don't seem to worried about them being sequential as you're creating new queues to execute each block, I suggest you use the global queues to run your blocks. Both actions are synonymous as these are final target queues for the dispatch queues you spawn. Getting the queue is simple and should replace the code where you create your own queue.
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
In case you want them to be run sequentially, you should define your own queue as an instance variable so that you create it only once and use the same one every time you need to dispatch a block.
Well you can maintain a Mutable dictionary with the objects added
[dict setobject:<your object> forkey:<the queue name>]
When you are sending subsequent request then in the method you can check the following:
object = [dict objectforkey:<queue name>]
if (object == nil)
//send the request
When the queue operation is complete remove the key-object pair from the dictionary.
[dict removeobjectforkey: < queue name >
No, there are no method to get a serial queue from the system by name. You need to have the serial queue by yourself.
Related
I define a property that returns a serial dispatch queue using lazy instantiation, something like this:
#property (nonatomic, readonly) dispatch_queue_t queue;
- (dispatch_queue_t)queue
{
if (!_queue) {
_queue = dispatch_queue_create("com.example.MyQueue", NULL);
}
return _queue;
}
Then let's say that I define an action method for some button that adds a block to the queue:
- (IBAction)buttonTapped:(UIButton *)sender
{
dispatch_async(self.queue, ^{
printf("Do some work here.\n");
});
}
The code for the actual method is more involved than a simple print statement, but this will do for the example.
So far so good. However, if I build and run the program, I can tap on the button 10 times and see the block run, but when I tap an eleventh time, the program hangs.
If I change the serial queue to a concurrent queue, no problems. I can dispatch as many blocks to the queue as I like.
Any idea what might be going on? Is there a limit to the number of blocks that can be posted to a serial queue?
In answer to the question on max blocks, I know of no practical limitation on what can be queued (other than available memory). Certainly, you should be able to queue far more than ten without incident.
But you have a typo in your queue getter method. You're setting _queue, but returning queue. You should return the same variable that you set. It looks like you must have two ivars defined; perhaps one that you defined manually and one that was synthesized? If you have a manually declared instance variable, you should just eliminate it and make sure your getter method is using the same instance variable, namely the one that was synthesized for you. Also, are you initializing this ivar in your init method?
If fixing this doesn't resolve the issue, then the problem probably rests in the particular code that you're dispatching to this queue and you should share that with us. Any synchronization code there? Any interaction with any shared resources?
OK, I finally resolved this issue.
First, when I reported that concurrent queues worked fine, but serial queues did not, I was mistaken. Both types of queues failed. When I observed everything working, that was actually using the main queue. So, in that case, there really wasn't any concurrency.
That said, the problem was a deadlock issue having to do with logging information via the main thread while I was processing on a secondary thread -- from either a serial or concurrent queue. To make matters worse, my app uses Lumberjack for logging which introduces additional threading issues. To resolve the matter, I wrapped every call to a Lumberjack logging method as follows:
dispatch_async(dispatch_get_main_queue(), ^{
// do logging here
});
That took care of the problem. Thanks for the comments. They ultimately led me to a solution.
I have a number of data so I want upload the data in the GCD queue order like FIFO method. How to do that ?
Whatever is your "upload" block, you must create a GCD serial queue and then dispatch_async all your upload blocks on it.
To create the queue:
dispatch_queue_t myFifoQueue = dispatch_queue_create("com.example.myfifoqueue",DISPATCH_QUEUE_SERIAL);
Once you have create your queue you can now dispatch your upload blocks.
To dispatch one of these blocks in the queue:
dispatch_async(myFifoQueue,myUploadBlock);
"dispatch_async" guarantees you that your blocks will be added in the serial queue but your current thread (usually the main thread) will not wait for the block to complete.
Using a serial queue guarantees you that all blocks will be executed in FIFO order.
E.g. if you have an NSArray *myArray and you want to process in the queue the array objects using a method called -(void)processObject:(id)object then you can write your code in this way:
for(id object in myArray) {
dispatch_async(myFifoQueue,^{
[self processObject:object];
}
);
}
Basically what you do here is to enumerate all objects in the array and then submit to the serial queue a simple block that calls the "processObject:" method. Whatever is the time taken by processObject to finish its task, dispatch_async will return immediately and the serial queue will process its blocks serially in a background thread.
Note that you don't have here a way to know when all blocks are done, so it is a good idea to submit at the end of the queue some block that notifies the main thread of the end of the queue (so that you can update your UI):
dispatch_async(myFifoQueue,^{
dispatch_async(dispatch_get_main_queue(),
^{
[self endOfUpload];
});
});
I have the following Method:
-(void) waitForStatusChangeAndPerformBlock:(MyBlockType)successBlock;
This is what the method should do:
Check if some status has the right value
If it does invoke the block successBlock
If not wait for the status to change to a given value and then invoke the block successBlock
I thought about KVO to check if the value has changed, but then I would have to store the block in some instance variable, or worse, an array, and I would loose the context of the current method call. So what I really want is something like this:
-(void) waitForStatusChangeAndPerformBlock:(MyBlockType)successBlock{
if(self.status == kDesiredStatus){
successBlock;
} else {
[TheMagicDispatchWaitUntil:(self.status == kDesiredStatus) andThenDoThis:^{
successBlock;
}];
}
}
Is there a way to achieve this without KVO or other helper methods?
If you want a theead to wait on an event - a message, timer, or whatever, one really nice way to do that is to use a Concurrent NSOperation. Those objects run on a separate thread, but have a runLoop so they can block in a the "normal" fashion inside the runloop callback waiting for something to happen.
That said, these do take a bit of finesse to get working. I have a demo project on gthub that lets you explore concurrent NSOperations (and there are others too).
Another nice way to block until something has done (on a thread) is to use "dispatch_wait()", which waits on all blocks that have been queued belonging to a group. This technique is pretty easy to pick up - you create a dispatch group and use the standard queues or create your own queue, then queue blocks using the dispatch_group functions. Once all are queued, you then dispatch_wait(forever) for the blocks to finish.
If you are doing just a simple routine and you don't have to call this method often, why don't you just use a while statement?
while (self.status != kDesiredStatus);
do {TheMagicDispatch}
succesBlock;
I am trying to learn more about dispatch queues. If I put three methods in a dispatch queue as in the code below, do they execute one after the other or all at once ?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^ {
[activeModel release];
[mainViewController showSceneList];
[mainViewController removeTidyUpScreen];
});
How would I specify that the next should not run until the previous one is completed ?
Think of a block -- the code you submit to a dispatch queue as you have here -- as an anonymous function. So, the code you have in your block here executes in order just as if you were calling a function that contained the same calls, one method, then the next, and so on.
In your particular example, it looks like you may be doing some operations with the UI on a queue that is not the main queue. You MUST do UI operations on the main queue, because it has access to the UI. You might use dispatch_get_main_queue() instead, to be sure you're getting that queue. If you have something you want to run in the background that will not touch the UI, then using a global queue is fine, and preferred especially if not stalling the UI is important.
I get a memory leak when the view controller calls my model class method at the line where i create my gcd queue. Any ideas?
+(void)myClassMethod {
dispatch_queue_t myQueue = dispatch_queue_create("com.mysite.page", 0); //run with leak instrument points here as culprit
dispatch_async(myQueue, ^{});
}
You should change it to ...
dispatch_queue_t myQueue = dispatch_queue_create("com.mysite.page", 0);
dispatch_async(myQueue, ^{});
dispatch_release(myQueue);
... you should call dispatch_release when you no longer need an access to the queue. And as myQueue is local variable, you must call it there.
Read dispatch_queue_create documentation:
Discussion
Blocks submitted to the queue are executed one at a time in FIFO order. Note, however, that blocks submitted to independent queues may be executed concurrently with respect to each other.
When your application no longer needs the dispatch queue, it should release it with the dispatch_release function. Any pending blocks submitted to a queue hold a reference to that queue, so the queue is not deallocated until all pending blocks have completed.
The Leak tool reports where memory is allocated that no longer has any references from your code.
After that method runs, since there is nothing that has a reference to the queue you created, and dispatch_release() was never called, it's considered a leak.