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];
});
});
Related
I would like to perform some code synchronously in the background, I really thought this is the way to go:
let queue = DispatchQueue.global(qos: .default)
queue.async {
print("\(Thread.isMainThread)")
}
but this prints true unless I use queue.async. async isn't possible as then the code will be executed in parallel. How can I achieve running multiple blocks synchronously in the background?
What I would like to achieve: synchronize events in my app with the devices calendar, which happens in the background. The method which does this can be called from different places multiple times so I would like to keep this in order and in the background.
Async execution isn't your problem, since you only care about the order of execution of your code blocks relative to each other but not relative to the main thread. You shouldn't block the main thread, which is in fact DispatchQueue.main and not DispatchQueue.global.
What you should do is execute your code on a serial queue asynchronously, so you don't block the main thread, but you still ensure that your code blocks execute sequentially.
You can achieve this using the following piece of code:
let serialQueue = DispatchQueue(label: "serialQueue")
serialQueue.async{ //call this whenever you need to add a new work item to your queue
//call function here
}
DispatchQueue is not equal to a Thread. Think of it as of a kind of abstraction over the thread pool.
That being said, main queue is indeed "fixed" on the main thread. And that is why, when you synchronously dispatch a work item from the main queue, you are still on the main thread.
To actually execute sync code in the background, you have to already be in the background:
DispatchQueue.global().async {
DispatchQueue.global().sync {
print("\(Thread.isMainThread)")
}
}
This will print false.
Also, as user #rmaddy correctly pointed out in comments, doing any expensive tasks synchronously from the main queue might result in your program becoming unresponsive, since the main thread is responsible for the UI updates.
{
dispatch_queue_t myQueue = dispatch_queue_create("com.mycompany.myqueue", 0);
dispatch_sync(myQueue, ^{
//Do EXTREME PROCESSING!!!
for (int i = 0; i< 100; i++) {
[NSThread sleepForTimeInterval:.05];
NSLog(#"%i", i);
}
dispatch_sync(dispatch_get_main_queue(), ^{
[self updateLabelWhenBackgroundDone];
});
});
}
I am getting a deadlock here. According to Apple documentation
"dispatch_sync": "Submits a block to a dispatch queue for synchronous
execution. Unlike dispatch_async, this function does not return until
the block has finished. Calling this function and targeting the
current queue results in deadlock.".
However, I do the outer dispatch_sync on myQueue and then I do inner ditpatch_sync on a different queue which is `main_queue.
Can not find out the reason for the deadlock. Any comments/help are appreciated here.
If you dispatch_sync to myQueue like that and the call happens on the main thread, then dispatch_sync will, if possible, execute the block right there and not on a new worker thread like dispatch_async would. You're not guaranteed to get a separate worker thread for your queue.
The block then runs on the main thread until it hits your second dispatch_sync call, which happens to target the main queue. That queue can't be serviced, since there's already a block running on it, and that's where you end up in a deadlock.
If that's your problem, i.e. the first dispatch_sync is indeed coming from the main thread, then you should switch to dispatch_async. You wouldn't want to block the main thread with the long-running "EXTREME PROCESSING" operation.
You are calling dispatch_sync twice. The first time suspends the main thread waiting for your block to complete. The block then suspends the background thread with the second call which tries to push back to the main thread (which will never process the block from its queue because it's suspended). Both threads are now waiting for each other.
At least one of the calls needs to be dispatch_async.
I had similar problems and none of these solutions worked. I asked someone smarter than me.
My problem was I was spawning a dispatching an async worker block, and then displaying a progress window. Calls back into the main thread via
dispatch_sync(dispatch_get_main_queue(), ^{})
failed as did async calls.
The explanation was that the main thread was no longer in 'commons mode' because of the modal window. I replaced my calls to the main thread with this....
CFRunLoopPerformBlock(([[NSRunLoop mainRunLoop] getCFRunLoop]), (__bridge CFStringRef)NSModalPanelRunLoopMode, ^{
//Update UI thread.
});
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 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.
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.