Let's say I have a serial dispatch queue and I enqueue several operations on it. I've read that I can't cancel operations once they are dispatched. Is it possible to at the very least view what GCD blocks I've dispatched to maybe make a decision if I want to dispatch another one?
Example, I dispatch Operation A to the queue but soon after my application decides to enqueue another Operation A, so now there are 2 of these operations queued up.
As Kevin Ballard said, you need to elaborate on what exactly you are trying to do. One thing you could do is set a flag, like valid_ and then you can effectively cancel all but the current item in the queue by doing something like this:
dispatch_async(queue, ^{
if (valid_) {
// perform your task here
}
});
Then whenever you want to "cancel" the queue, just set your valid_ flag to NO.
Again though, give more info on what you are trying to do and I can give you a better answer.
Since NSOperation is now built on top of GCD, you can now use addOperationWithBlock: to put your block on an NSOperationQueue, then you can invoke operations on the NSOperationQueue to get an NSArray of unfinished operations.
The problem with this, is that this is more than two operations and is not atomic, so it's entirely possible that your operation will finish in between the time you get the operations array and see if it's contained there.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperationQueue_class/Reference/Reference.html
NSOperations have a prerequisite API, however, so you can enqueue another operation which will only run if your first NSOperation finishes, and use this to keep track of when you should try to enqueue your first NSOperation again.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html#//apple_ref/doc/uid/TP40004591
Related
im studying multithreading and what i want is some clarification on subject matter.
As far as i know, SERIAL queue execute tasks serially, are always executing one task at a time.
Now, SYNCHRONOUS function is a function, that returns only after all tasks complete.
Now, i'm a bit confused. What difference between those two?
if i understand correct, both of them will block current thread (if they are not "covered" in global concurrent queue), and both of them execute tasks exactly in FIFO order.
So, what exactly a difference between them? Yes, i understand that serial is a property of a queue, and sync is a function (or operation). But their functionality is like to be similiar.
You are comparing a queue with a function, so it is difficult to define "difference". Using a serial queue does guarantee sequential behaviour of its operations. Typically, you use a synchronous dispatch if your program has to wait for all queued operations to complete before your program completes. If every dispatch on a given queue is synchronous, then indeed there is no difference between using a queue or calling the operations.
However, here is a very useful case that shows the difference. Suppose operation A is lengthy and you do not want to block. Suppose operation B returns something computed by operation A, but it is called some arbitrary time later (like in response to a user action). You dispatch_async A onto the queue. Your program is not blocked. Sometime later, you need the result. You dispatch_sync operation B on the same serial queue.
Now if A is already complete, the queue is empty when you add B and B executes immediately. But (and here is the good part) if A is still executing (asynchronously), B is not dispatched until A is done, so your program is blocked until the result it needs is ready.
For more explanation of this, see here.
The dangers of deadlock nicely handled for you by gcd.
What happens if you dispatch_async a block of code on a queue that's currently blocked by it's own dispatch_sync operation? Do they lock or will the blocked queue continue after the dispatch_sync operation returns?
I have an object I created that manages access to a backing store (SQLite, in this case). It uses one concurrent GCD queue and any other objects that want to access the information from the store will pass a request to the manager along with a block that will be executed asynchronously. The essence of what happens is this (not actual code):
- (void) executeRequest:(StoreRequest *)request withCompletionBlock:(void(^)(NSInteger result)block{
dispatch_queue_t currentContext = dispatch_get_current_queue();
dispatch_async(_storeQueue, ^{
NSInteger result = [_store executeRequest:request];
if (block){
dispatch_async(currentContext, ^{
block(result);
}
}
});
}
The real code is a bit more complex (I actually queue up and store requests/blocks/contexts to execute at the end of a run loop). I also use dispatch_barrier_async for write requests to prevent concurrent read/writing. This all works fine, but in certain situations I also need to perform a synchronous request on the store. Now this request doesn't need to be performed before any queued up operations, but I do need the requesting queue blocked until the operation is performed. This can be easily done:
- (NSInteger) executeRequest:(StoreRequest *)request{
__block NSInteger result = 0;
dispatch_sync(_storeQueue, ^{
result = [_store executeRequest:request];
});
return result;
}
My question is this: What happens if a pending asynchronous operation placed before the synchronous operation dispatches a block of code asynchronously on the queue that is currently blocked by the synchronous dispatch. In other words, the above operation will dispatch its request at the end of the _store queue and wait. But it's quite possible (even likely) that the operations in front of it include asynchronous dispatches back to the waiting queue (for other operations). Will this lock the threads? Since the queued blocks are dispatched asynchronously the _store queue will never be blocked and therefore will finish, theoretically allowing the queue it's blocking to continue...but I'm not sure what happens with the blocks that were asynchronously dispatched or if dispatching anything to a block thread locks it up. I would assume that the blocked queue will continue, finish it's request and then the process the pending blocks, but I want to make sure.
Actually, now that I've written this all up, I'm pretty sure it'll work just fine, but I'm going to post this question anyway to make sure I'm not missing anything.
dispatch_async never blocks. It's that simple.
The dispatch_async itself never blocks. It appends the block to the end of the queue and returns immediately.
Will the block get executed? It depends. In a sequential queue, if one block is blocked, no other block will execute until that block gets unblocked and finishes. On a background queue, the queue can use multiple threads, so even if some blocks are blocked, it will just start other blocks. I haven't tried if there is a limit to the number of blocked blocks, but there's a good chance that all unblocked blocks will eventually execute and finish, and you are left with the blocked ones.
First I create a serial queue like this
static dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
then, at some unknown point in time a task gets added to the queue like this
dispatch_async(queue, ^{
// do something, which takes some time
});
If the first task hasn't finished yet, the new task will wait until the first completes (that's of course what a serial queue is for).
But if I add 5 new tasks to the queue, while the original first one is still running, I don't want to execute new task no.1, then no.2, then no.3 and so on, but want to get rid of tasks 1 to 4 and directly start executing task no.5 after the original first task has finished.
In other words, I want to pop any waiting task (not the one that is currently running) off the queue, if I add a new one.
Is there a build in mechanism for this or do I have to implement this myself? And for the latter, how would I identify single tasks inside a queue and remove them?
Once a block has been submitted to a GCD dispatch queue, it will run. There is no way to cancel it. You can, as you know, implement your own mechanism to "abort" the block execution early.
An easier way to do this would be to use NSOperationQueue, as it already provides an implementation for canceling pending operations (i.e., those not yet running), and you can easily enqueue a block with the new-ish addOperationWithBlock method.
Though NSOperationQueue is implemented using GCD, I find GCD much easier to use in most cases. However, in this case, I would seriously consider using NSOperationQueue because it already handles canceling pending operations.
With Davids answer getting me on track I succeeded in doing this like so
taskCounter++;
dispatch_async(queue, ^{
if (taskCounter > 1) {
taskCounter--;
NSLog(#"%#", #"skip");
return;
}
NSLog(#"%#", #"start");
// do stuff
sleep(3);
taskCounter--;
NSLog(#"%#", #"done");
});
taskCounter has to be either an ivar or a property (initialize it with 0). In that case it doesn't even need the __block attribute.
The way you handle this is to use an ivar that indicates to the queued blocks they should just return:
^{
if(!canceled) {
... do work
}
}
You don't need to use a simple boolean either - you can make this more complex - but the general idea is to use one or more ivars that the block queries before doing anything.
I use this technique (but did not invent it) with great success.
If instead of adding a closure in you add a DispatchWorkItem, you can cancel it as long as it hasn't started executing yet.
In the following code, backgroundWorkItem will never run, because it is cancelled before it starts executing.
let backgroundWorkItem = DispatchWorkItem {
print("Background work item executed")
}
DispatchQueue.main.async(execute: backgroundWorkItem)
backgroundWorkItem.cancel()
I'm executing the following method :
MotionHandler.m
-(void)startAccelerationUpdates
{
[motionManagerstartDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]withHandler:^(CMDeviceMotion *motion, NSError *error){.....}
}
on a background thread, as follows:
[currentMotionHandler performSelectorInBackground:#selector(startAccelerationUpdates) withObject:nil];
But the above method uses the main Queue (which is on the main thread) to perform the necessary updates even though I'm calling it on a background thread.. So are acceleration updates being performed on a background thread or on the main thread, I'm confused..?
What's even more interesting is that when I call the above method on background thread again, but this time using the current Queue, I get no updates. Could someone please explain the difference between running something on :
1. a background thread but on the main queue
2. a background thread but on the current queue
3. the main thread but on the main queue
4. the main thread but on the current queue
in the current implementation? Thank you!
I'll give it a shot. First, without being told by the NSOperationQueue class reference, we could not infer anything about what thread the 'mainQueue' would run on. Reading it we see that in fact that queue runs its operations on the mainThread, the one the UI uses, so you can update the UI in operations posted to that queue. Although it doesn't say it, these operations must be serial, due to them being executed by the runLoop (its possible they can get preempted too, not 100% sure of that).
The purpose for currentQueue is so that running operations can determine the queue they are on, and so they can potentially queue new operations on that queue.
a background thread but on the main queue
Not possible - the NSOperation's mainQueue is always associated with the mainThread.
a background thread but on the current queue
When you create a NSOperationQueue, and add NSOperations to it, those get run on background threads managed by the queue. Any given operation can query what thread its on, and that thread won't change while it runs. That said, a second operation on that queue may get run on a different thread.
the main thread but on the main queue
See 1)
the main thread but on the current queue
If you queue an operation to the mainQueue (which we know is always on the mainThread), and you ask for the currentQueue, it will return the mainQueue:
[NSOperationQueue currentQueue] == [NSOperationQueue mainQueue];
You are confusing queues with threads. Especially since NSOpertionQueue has been rewritten to use GCD, there is little connection between queues and specific threads (except for the special case of the main thread).
Operations/blocks/tasks - whatever you want to call them - are inserted into a queue, and "worker thread(s)" pull these off and perform them. You have little control over which exact thread is going to do the work -- except for the main queue. Note, this is not exactly right, because it's a simplification, but it's true enough unless you are doing something quite advanced and specific.
So, none of your 4 scenarios even make sense, because you can't, for example, run something on "a background thread but on the main queue."
Now, your method startAccelerationUpdates specifically tells the CMMotionManager to put your handler on the main queue. Thus, when startAccelerationUpdates is called, it gets run in whichever thread it's running, but it schedules the handler to be executed on the main thread.
To somewhat complicate things, you are calling the startAccelerationUpdates method by calling performSelectorInBackground. Again, you don't know which thread is going to actually invoke startAccelerationUpdates, but it will not be the main thread.
However, in your case, all that thread is doing is calling startAccelerationUpdates which is starting motion updates, and telling them to be handled on the main thread (via the main queue).
Now, here's something to dissuade you from using the main queue to handle motion events, directly from the documentation...
Because the processed events might arrive at a high rate, using the main operation queue is not recommended.
Unfortunately, your statement
What's even more interesting is that when I call the above method on
background thread again, but this time using the current Queue, I get
no updates.
does not provide enough information to determine what you tried, how you tried it, or why you think it did not work. So, I'll make a guess... which may be wrong.
I'll key on your use of the current Queue.
I assume you mean that you substitute [NSOperationQueue mainQueue] with [NSOperationQueue currentQueue].
Well, let's see what that does. Instead of using the main queue, you will be using "some other" queue. Which one? Well, let's look at the documentation:
currentQueue
Returns the operation queue that launched the current
operation.
+ (id)currentQueue
Return Value
The operation queue that started the operation or nil if the queue could not be determined.
Discussion
You can use this method from within a running operation
object to get a reference to the operation queue that started it.
Calling this method from outside the context of a running operation
typically results in nil being returned.
Please note the discussion section. If you call this when you are not running an operation that was invoked from an NSOperationQueue, you will get nil which means there will be no queue on which to place your handler. So, you will get nothing.
You must specify which queue is to be used, if you want to use an NSOperationQueue other than the main queue. So, if that's the route you want to go, just create your own operation queue to handle motion events, and be off!
Good Luck!
I ran into a scenario where I had a delegate callback which could occur on either the main thread or another thread, and I wouldn't know which until runtime (using StoreKit.framework).
I also had UI code that I needed to update in that callback which needed to happen before the function executed, so my initial thought was to have a function like this:
-(void) someDelegateCallback:(id) sender
{
dispatch_sync(dispatch_get_main_queue(), ^{
// ui update code here
});
// code here that depends upon the UI getting updated
}
That works great, when it is executed on the background thread. However, when executed on the main thread, the program comes to a deadlock.
That alone seems interesting to me, if I read the docs for dispatch_sync right, then I would expect it to just execute the block outright, not worrying about scheduling it into the runloop, as said here:
As an optimization, this function invokes the block on the current thread when possible.
But, that's not too big of a deal, it simply means a bit more typing, which lead me to this approach:
-(void) someDelegateCallBack:(id) sender
{
dispatch_block_t onMain = ^{
// update UI code here
};
if (dispatch_get_current_queue() == dispatch_get_main_queue())
onMain();
else
dispatch_sync(dispatch_get_main_queue(), onMain);
}
However, this seems a bit backwards. Was this a bug in the making of GCD, or is there something that I am missing in the docs?
dispatch_sync does two things:
queue a block
blocks the current thread until the block has finished running
Given that the main thread is a serial queue (which means it uses only one thread), if you run the following statement on the main queue:
dispatch_sync(dispatch_get_main_queue(), ^(){/*...*/});
the following events will happen:
dispatch_sync queues the block in the main queue.
dispatch_sync blocks the thread of the main queue until the block finishes executing.
dispatch_sync waits forever because the thread where the block is supposed to run is blocked.
The key to understanding this issue is that dispatch_sync does not execute blocks, it only queues them. Execution will happen on a future iteration of the run loop.
The following approach:
if (queueA == dispatch_get_current_queue()){
block();
} else {
dispatch_sync(queueA, block);
}
is perfectly fine, but be aware that it won't protect you from complex scenarios involving a hierarchy of queues. In such case, the current queue may be different than a previously blocked queue where you are trying to send your block. Example:
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
// dispatch_get_current_queue() is B, but A is blocked,
// so a dispatch_sync(A,b) will deadlock.
dispatch_sync(queueA, ^{
// some task
});
});
});
For complex cases, read/write key-value data in the dispatch queue:
dispatch_queue_t workerQ = dispatch_queue_create("com.meh.sometask", NULL);
dispatch_queue_t funnelQ = dispatch_queue_create("com.meh.funnel", NULL);
dispatch_set_target_queue(workerQ,funnelQ);
static int kKey;
// saves string "funnel" in funnelQ
CFStringRef tag = CFSTR("funnel");
dispatch_queue_set_specific(funnelQ,
&kKey,
(void*)tag,
(dispatch_function_t)CFRelease);
dispatch_sync(workerQ, ^{
// is funnelQ in the hierarchy of workerQ?
CFStringRef tag = dispatch_get_specific(&kKey);
if (tag){
dispatch_sync(funnelQ, ^{
// some task
});
} else {
// some task
}
});
Explanation:
I create a workerQ queue that points to a funnelQ queue. In real code this is useful if you have several “worker” queues and you want to resume/suspend all at once (which is achieved by resuming/updating their target funnelQ queue).
I may funnel my worker queues at any point in time, so to know if they are funneled or not, I tag funnelQ with the word "funnel".
Down the road I dispatch_sync something to workerQ, and for whatever reason I want to dispatch_sync to funnelQ, but avoiding a dispatch_sync to the current queue, so I check for the tag and act accordingly. Because the get walks up the hierarchy, the value won't be found in workerQ but it will be found in funnelQ. This is a way of finding out if any queue in the hierarchy is the one where we stored the value. And therefore, to prevent a dispatch_sync to the current queue.
If you are wondering about the functions that read/write context data, there are three:
dispatch_queue_set_specific: Write to a queue.
dispatch_queue_get_specific: Read from a queue.
dispatch_get_specific: Convenience function to read from the current queue.
The key is compared by pointer, and never dereferenced. The last parameter in the setter is a destructor to release the key.
If you are wondering about “pointing one queue to another”, it means exactly that. For example, I can point a queue A to the main queue, and it will cause all blocks in the queue A to run in the main queue (usually this is done for UI updates).
I found this in the documentation (last chapter):
Do not call the dispatch_sync function from a task that is executing
on the same queue that you pass to your function call. Doing so will
deadlock the queue. If you need to dispatch to the current queue, do
so asynchronously using the dispatch_async function.
Also, I followed the link that you provided and in the description of dispatch_sync I read this:
Calling this function and targeting the current queue results in deadlock.
So I don't think it's a problem with GCD, I think the only sensible approach is the one you invented after discovering the problem.
I know where your confusion comes from:
As an optimization, this function invokes the block on the current
thread when possible.
Careful, it says current thread.
Thread != Queue
A queue doesn't own a thread and a thread is not bound to a queue. There are threads and there are queues. Whenever a queue wants to run a block, it needs a thread but that won't always be the same thread. It just needs any thread for it (this may be a different one each time) and when it's done running blocks (for the moment), the same thread can now be used by a different queue.
The optimization this sentence talks about is about threads, not about queues. E.g. consider you have two serial queues, QueueA and QueueB and now you do the following:
dispatch_async(QueueA, ^{
someFunctionA(...);
dispatch_sync(QueueB, ^{
someFunctionB(...);
});
});
When QueueA runs the block, it will temporarily own a thread, any thread. someFunctionA(...) will execute on that thread. Now while doing the synchronous dispatch, QueueA cannot do anything else, it has to wait for the dispatch to finish. QueueB on the other hand, will also need a thread to run its block and execute someFunctionB(...). So either QueueA temporarily suspends its thread and QueueB uses some other thread to run the block or QueueA hands its thread over to QueueB (after all it won't need it anyway until the synchronous dispatch has finished) and QueueB directly uses the current thread of QueueA.
Needless to say that the last option is much faster as no thread switch is required. And this is the optimization the sentence talks about. So a dispatch_sync() to a different queue may not always cause a thread switch (different queue, maybe same thread).
But a dispatch_sync() still cannot happen to the same queue (same thread, yes, same queue, no). That's because a queue will execute block after block and when it currently executes a block, it won't execute another one until the currently executed is done. So it executes BlockA and BlockA does a dispatch_sync() of BlockB on the same queue. The queue won't run BlockB as long as it still runs BlockA, but running BlockA won't continue until BlockB has ran. See the problem? It's a classical deadlock.
The documentation clearly states that passing the current queue will cause a deadlock.
Now they don’t say why they designed things that way (except that it would actually take extra code to make it work), but I suspect the reason for doing things this way is because in this special case, blocks would be “jumping” the queue, i.e. in normal cases your block ends up running after all the other blocks on the queue have run but in this case it would run before.
This problem arises when you are trying to use GCD as a mutual exclusion mechanism, and this particular case is equivalent to using a recursive mutex. I don’t want to get into the argument about whether it’s better to use GCD or a traditional mutual exclusion API such as pthreads mutexes, or even whether it’s a good idea to use recursive mutexes; I’ll let others argue about that, but there is certainly a demand for this, particularly when it’s the main queue that you’re dealing with.
Personally, I think that dispatch_sync would be more useful if it supported this or if there was another function that provided the alternate behaviour. I would urge others that think so to file a bug report with Apple (as I have done, ID: 12668073).
You can write your own function to do the same, but it’s a bit of a hack:
// Like dispatch_sync but works on current queue
static inline void dispatch_synchronized (dispatch_queue_t queue,
dispatch_block_t block)
{
dispatch_queue_set_specific (queue, queue, (void *)1, NULL);
if (dispatch_get_specific (queue))
block ();
else
dispatch_sync (queue, block);
}
N.B. Previously, I had an example that used dispatch_get_current_queue() but that has now been deprecated.
Both dispatch_async and dispatch_sync perform push their action onto the desired queue. The action does not happen immediately; it happens on some future iteration of the run loop of the queue. The difference between dispatch_async and dispatch_sync is that dispatch_sync blocks the current queue until the action finishes.
Think about what happens when you execute something asynchronously on the current queue. Again, it does not happen immediately; it puts it in a FIFO queue, and it has to wait until after the current iteration of the run loop is done (and possibly also wait for other actions that were in the queue before you put this new action on).
Now you might ask, when performing an action on the current queue asynchronously, why not always just call the function directly, instead of wait until some future time. The answer is that there is a big difference between the two. A lot of times, you need to perform an action, but it needs to be performed after whatever side effects are performed by functions up the stack in the current iteration of the run loop; or you need to perform your action after some animation action that is already scheduled on the run loop, etc. That's why a lot of times you will see the code [obj performSelector:selector withObject:foo afterDelay:0] (yes, it's different from [obj performSelector:selector withObject:foo]).
As we said before, dispatch_sync is the same as dispatch_async, except that it blocks until the action is completed. So it's obvious why it would deadlock -- the block cannot execute until at least after the current iteration of the run loop is finished; but we are waiting for it to finish before continuing.
In theory it would be possible to make a special case for dispatch_sync for when it is the current thread, to execute it immediately. (Such a special case exists for performSelector:onThread:withObject:waitUntilDone:, when the thread is the current thread and waitUntilDone: is YES, it executes it immediately.) However, I guess Apple decided that it was better to have consistent behavior here regardless of queue.
Found from the following documentation.
https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html#//apple_ref/c/func/dispatch_sync
Unlike dispatch_async, "dispatch_sync" function does not return until the block has finished. Calling this function and targeting the current queue results in deadlock.
Unlike with dispatch_async, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy is performed on the block.
As an optimization, this function invokes the block on the current thread when possible.