Updating UI element without dispatching to the main queue - ios

I have a dispatch_block_t which is passed to another function, and this block will be called when the function finishes the asynchronous task. But the problem is that I don't know which thread this block will be called.
I want to update my UI in the main thread, hence I want to use
dispatch_async(dispatch_get_main_queue(), ^{...})
to update my UI. But I am afraid that this will cause a deadlock if such occasion happens
dispatch_queue_t queue = dispatch_queue_create("my.label", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
dispatch_async(queue, ^{
// outer block is waiting for this inner block to complete,
// inner block won't start before outer block finishes
// => deadlock
});
// this will never be reached
});
Is there a way to prevent the deadlock? Like updating the UI element without using the dispatch queue. Is it possible to create a weak reference to self in order to update the UI?

Try running your example with NSLogs and you'll notice that deadlock doesn't occur. This is due to the fact that using dispatch_async just submits a block to the queue without waiting for it to finish execution (in contrary to dispatch_sync).
So running this code:
dispatch_queue_t queue = dispatch_queue_create("my.label", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(#"1");
dispatch_async(queue, ^{
NSLog(#"2");
});
NSLog(#"3");
});
Will produce the following log:
Testtt[32153:2250572] 1
Testtt[32153:2250572] 3
Testtt[32153:2250572] 2
Moreover, I'm concerned that using dispatch_async(dispatch_get_main_queue(), ^{...}) here is commonly-used technique which ensures that the consumer gets the result on the main thread (i.e. consumer doesn't 'care' about threading).
Why, though, you use dispatch_block_t to pass a completion block? In my opinion, it's a bit confusing to use something like that on the consumer side - I would pass an anonymous (without typedef) block or create my own typedef for these simple completion blocks.

Related

Performing UI updates on main thread synchronously from a concurrent queue

As far as I have understood GCD UI operations should always be performed on the main thread/main queue asynchronously. But the following code seems to also work without any problem. Can someone please explain why ?
I am passing 2 blocks synchronously to a dispatch_async. One block downloads an image and the other displays it on the view.
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
__block UIImage *image = nil;
dispatch_sync(concurrentQueue, ^{
/* Download the image here */
});
dispatch_sync(dispatch_get_main_queue(), ^{
/* Show the image to the user here on the main queue */
});
});
The queue is important (it has to be the main queue) but whether the gcd calls are synchronous or asynchronous is irrelevant - that just affects how the rest of your code around the gcd calls is timed. Once a block is running on a queue it doesn't matter how it was scheduled.
Synchronous dispatch can simplify your code (since it won't return until the block is executed) but does come with the risk of locking if you end up waiting for things to finish.

what the benefit of using dispatch_sync ? ios

Is there a difference between these two implementation :
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
__block UIImage *image = nil;
dispatch_sync(concurrentQueue, ^{
/* Download the image here sync downloading */
});
dispatch_sync(dispatch_get_main_queue(), ^{
/* Show the image to the user here on the main queue */
});
});
and
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
__block UIImage *image = nil;
/* Download the image here sync downloading */
dispatch_sync(dispatch_get_main_queue(), ^{
/* Show the image to the user here on the main queue */
});
});
In the first code, I download the image using a block by dispatch sync, and in the second one I download it without block!
I think I should get deadlock in the first implementation because Apple says: dispatch_sync->Calling this function and targeting the current queue results in deadlock
I think I should get deadlock in the first implementation because apple say :dispatch_sync->Calling this function and targeting the current queue results in deadlock
It's true if the queue is a serial queue. However, in this case, you are using global queue, thus deadlock never happen. So the difference is which thread will do Download the image here sync downloading in the global queue thread pool.
First implementation
main queue -----------+---------------------------------+-----------+------
async(global)| ^ |
v sync(main)| v
global queue thread1 +-----+BLOCKED +-----------+BLOCKED +------
sync(global)| ^
v |
global queue thread2 +---------------+
Second implementation
main queue -----------+---------------------------------+-----------+------
async(global)| ^ |
v sync(main)| v
global queue thread1 +---------------------------------+BLOCKED +------
But dispatch_sync is not good idea in this case. https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
Important: You should never call the dispatch_sync or dispatch_sync_f function from a task that is executing in the same queue that you are planning to pass to the function. This is particularly important for serial queues, which are guaranteed to deadlock, but should also be avoided for concurrent queues.
... Even though I can't find any evidence of dispatch_sync should also be avoided for concurrent queues from
http://opensource.apple.com/source/libdispatch/libdispatch-339.92.1/src/queue.c
dispatch_sync dispatch_get_main_queue() done the action immediately.Because we are doing the cation in main queue.Here we are doing the UI updates and all that stuffs in lazy loading and asynchronous download etc.
Its related to GCD for more details please refer AppleDevelopers forum.
The main benefits of using dispatch_sync is we can do the operation concurrently.

Simple GCD Serial Queue example like FIFO using blocks

I read Apple documentation on how to Use serial queues to ensure that tasks to execute in a predictable order but now i am confused too much.
Some how i am able to work serially but still i am not clear so i need simple serial example for my methods to execute serially.
I divided my functionality in to 4 parts and now want them to execute Serially
[self ReadAllImagesFromPhotosLibrary];
[self WriteFewImagestoDirectory];
[self GettingBackAllImagesFromFolder];
[self MoveToNextView];
To follow-up and improve iCoder's answer, you could and should do the following.
dispatch_queue_t serialQueue = dispatch_queue_create("com.unique.name.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
[self ReadAllImagesFromPhotosLibrary];
});
dispatch_async(serialQueue, ^{
[self WriteFewImagestoDirectory];
});
dispatch_async(serialQueue, ^{
[self GettingBackAllImagesFromFolder];
});
dispatch_async(serialQueue, ^{
[self MoveToNextView];
});
Despite the above calls being async, they will be queued and run serially as the DISPATCH_QUEUE_SERIAL states. The difference between sync and async is that with sync, your code will pause and wait for the block answer before running the following code, thus potentially freezing your UI if the execution time is long. Whereas with async, the code runs on and the block is returned asynchronously.
However, the tasks you have stored in the DISPATCH_QUEUE_SERIAL will wait and be executed one after the other in the order they were added, thanks to GCD (Grand Central Dispatch).
dispatch_queue_t serialQueue = dispatch_queue_create("com.unique.name.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
[self ReadAllImagesFromPhotosLibrary];
dispatch_async(serialQueue, ^{
[self WriteFewImagestoDirectory];
dispatch_async(serialQueue, ^{
[self GettingBackAllImagesFromFolder];
dispatch_async(serialQueue, ^{
[self MoveToNextView];
});
});
});
});
I think the above code should work, but make sure the UI operations are executed in the main thread. Hope it helps.
You can use NSOperationQueue with maxConcurrentOperationCount set to 1 (or even set dependency for each NSOperation, so it won't start before its dependency is finished).
Here is NSOperationQueue Class Reference.
Also take a look at this question.
I am not much aware of existing API for doing the same with blocks, if any.
But the same can be done by defining blocks(representing the operations you want) in a fashion that they point to next block to proceed if any. Also, you can put the whole processing in a separate queue.
snippet for having blocks executing in serial fashion
BLOCK A(NEXT BLOCK reference){
->Do the the required Task
->If(next Block reference)
--->Then call that block
->Else
--->Exit or have a callback on mainthread
}
why not try the GCD, it guarantees the sequence of operation and also has sync and async capabilities
I had some success with a pattern like this in a similar hunt in Swift 3.0 ...
let serialQueue = DispatchQueue.init(label: "com.foo.bar")
serialQueue.sync {self.readAllImagesFromPhotosLibrary()}
serialQueue.sync {self.rriteFewImagestoDirectory()}
serialQueue.sync {self.gettingBackAllImagesFromFolder()}
serialQueue.sync {self.moveToNextView()}

Delay in executing method after dispatch group operations are complete in iOS

I am calling a method after after the queues in my dispatch group complete executing. However, there is a significant delay in executing the final method even after all the queues have been executed. Can anyone explain any probable reasons?
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue,^{
//some code
}
dispatch_group_notify(group, queue,
^{
[self allTasksDone];
});
What I meant was that the method allTasksDone is executed after some delay even when the operation in the async queue has completed.
How does -allTasksDone work? If it's communicating with the user by updating user interface elements, it need to run on in the main thread's context, or else it'll appear that the UI elements in question are "delayed" -- they won't update until the main run loop happens to make them update.
Try this instead:
dispatch_group_notify(group, dispatch_get_main_queue(),
^{
[self allTasksDone];
});
As it is, you're running -allTasksDone on the default background queue, which doesn't play nice with AppKit or UIKit.
I suggest an alternative approach although you can most certainly accomplish this using dispatch groups.
// Important note: This does not work with global queues, but you can use target queues to direct your custom queue to one of your global queues if you need priorities.
dispatch_queue_t queue = dispatch_queue_create("com.mycompany.myqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue,^{
//some code
}
dispatch_barrier_async(queue,
^{
// this executes when all previously dispatched blocks have finished.
[self allTasksDone];
});

iOS Ensuring a Function Is Called Only Once Per Session

I have several threads and once they are all finished working, I need to call a myMergeBlock method exactly once per action. I can't use dispatch_once because I want to be able to call myMergeBlock at a later time.
Some pseudo code looks like this but is not yet thread safe:
BOOL worker1Finished, worker2Finished, worker3Finished;
void (^mergeBlock)(void) = ^{
if (worker1Finished && worker2Finished && worker3Finished)
dispatch_async(queue, myMergeBlock); // Must dispatch this only once
}
void (^worker1)(void) = ^{
...
worker1Finished = YES;
mergeBlock();
}
void (^worker2)(void) = ^{
...
worker2Finished = YES;
mergeBlock();
}
void (^worker3)(void) = ^{
...
worker3Finished = YES;
mergeBlock();
}
Also, based on the way the workers are called, I do not call them directly, but instead pass them into a function as arguments.
You want to use dispatch groups. First you create a group, schedule the three workers in the group, then add a notification block to the group.
It should look something like this:
//create dispatch group
dispatch_group_t myWorkGroup = dispatch_group_create();
//get one of the global concurrent queues
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);
//submit your work blocks
dispatch_group_async(myWorkGroup, myQueue, worker1);
dispatch_group_async(myWorkGroup, myQueue, worker2);
dispatch_group_async(myWorkGroup, myQueue, worker3);
//set the mergeBlock to be submitted when all the blocks in the group are completed
dispatch_group_notify(myWorkGroup, myQueue, mergeBlock);
//release the group as you no longer need it
dispatch_release(myWorkGroup);
You could hang on the the group and reuse it later if you prefer. Be sure to schedule the work before the notification. If you try to schedule the notification first it will be dispatched immediately.
I haven't tested this code but I do use dispatch_groups in my projects.
This sounds very messy and low level. Have you looked at Operation Queues and Dispatch Groups and semaphores as discussed in the Concurrency Programming Guide. I think they may offer simpler solutions to your problem.
If you're targeting Lion or iOS 5 and up, you can use barrier blocks as long as the blocks are dispatched on a non-global, concurrent queue. For example:
dispatch_queue_t customConcurrentQueue = dispatch_queue_create("customQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(customConcurrentQueue, worker1);
dispatch_async(customConcurrentQueue, worker2);
dispatch_async(customConcurrentQueue, worker3);
dispatch_barrier_async(customConcurrentQueue, mergeBlock);
//Some time later, after you're sure all of the blocks have executed.
dispatch_queue_release(customConcurrentQueue);
A barrier block executes after all previously submitted blocks have finished executing, and any blocks submitted after the barrier block will be forced to wait until the barrier block has finished. Again, for reasons which should be obvious, you can't use barrier blocks on the global queues. You must create your own concurrent queue.

Resources