iPhone - canceling queued operations GCD
I have several tasks that are dispatched to serial queues and specially some that are dispatched to a group. After these tasks are dispatched I would like to give the user the option to cancel them, even if they are already being executed.
I cannot find any way to cancel a queued task, then I imagined I could create a boolean flag, called for example, quitRender and test to see if this flag if true and quit the render, but this is not working and my explanation is: the flag quitRender is NO when the tasks are fired, so as they are executed in blocks and blocks freeze the values as they were when they start, the blocks will not see the change and will never quit.
Then I tried another approach: I create a method like
- (BOOL) cancelRender {
return quitRender;
}
that would run and send the block the current value of the variable, but apparently the blocks continue to see开发者_开发知识库 NO for quitRender.
Am I missing something? How to I make this work?
thanks.
__block storage modifier may be helpful in such a situation.
__block BOOL quitRender = NO;
It makes to be able to modify the variable from blocks and blocks can see the modified variable. But be careful that accessing __block variable is not thread-safe.
atomic functions are preferred to ensure accessing a variable as atomic.
Also you can use ivar from blocks. If the variable is a property with atomic attribute, it is thread-safe to access.
How do you use quitRender variable?
EDIT:
__block int32_t quitRender = 0;
/* set */
OSAtomicOr32(1, &quitRender);
/* test */
if (quitRender) {
You're on the right track with making the quitRender variable a __block
storage class variable, since then the main body of the code can modify it to indicate that the pending operation should be cancelled.
There is no issue with making it thread safe, however, as others have suggested, since cancellation is inherently racey to begin with. You could start executing the block (and do the cancel check) immediately before the variable is set to TRUE, in which case you'd lose the race even if you were doing the OSAtomic()
wrapping, so you might just as well set it and forget it since the cancellation flag can only move from the FALSE to TRUE state in this case.
If managing the block class storage is a pain for some reason (say you have multiple operations all potentially cancelable) then put the cancellation flag inside whatever data structure is being managed by the queue and simply check it. If the queue itself is the right place to indicate that all operations on the queue are cancelled, then you can also use dispatch_{set,get}_specific()
to store this with the queue itself. HTH.
If you want to quit all tasks of a given class, you could try making the quitRender flag a volatile static BOOL global variable, which the block code shouldn't freeze.
You can wrap getters and setters around static global variables to make the code cleaner.
精彩评论