How does dispatch_barrier_async interact with the target queue? - ios

Given the creation of queue2 with target of queue1 (queue2 = dispatch_queue_create_with_target(name, attr, queue1))...
If both queues are concurrent, does dispatch_barrier_async on queue2 only wait on queue2 to be empty, or does it also wait for the target queue? When both queues have respective barrier blocks queued, does queue2's barrier block take priority?

A barrier on queue does not affect its target queue.
This is most easily demonstrated empirically. For example:
- (void)experiment {
dispatch_queue_t queue1 = dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create_with_target("2", DISPATCH_QUEUE_CONCURRENT, queue1);
dispatch_async(queue1, ^{
[self taskOnQueue:1 taskNumber:1 color:1];
});
dispatch_async(queue2, ^{
[self taskOnQueue:2 taskNumber:2 color:0];
});
dispatch_barrier_async(queue2, ^{
[self taskOnQueue:2 taskNumber:3 color:0];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_async(queue2, ^{
[self taskOnQueue:2 taskNumber:4 color:0];
});
dispatch_async(queue1, ^{
[self taskOnQueue:1 taskNumber:5 color:1];
});
});
}
Tasks 1-3 are immediately dispatched, and tasks 4 and 5 are dispatched 0.5 seconds later. Task 3 is using a barrier. Tasks 1 and 5 are on queue1, and tasks 2-4 are on queue2. All tasks take one second each.
That results in the following. (I manually highlighted those task numbers to make this more clear.)
You can see that task #5 on queue 1 starts as soon as it's queued, even though (a) it was the last task queued, and (b) queue 2 has a barrier on task #3. The second queue respects the barrier on task 3, though.
FYI, this these are the utility methods that generate those points of interest ranges:
- (void)taskOnQueue:(uint32_t)code taskNumber:(uint32_t)arg1 color:(uint32_t)arg4 {
[self pointOfInterest:code arg1:arg1 color:arg4 block:^{
[NSThread sleepForTimeInterval:1];
}];
}
- (void)pointOfInterest:(uint32_t)code arg1:(uint32_t)arg1 color:(uint32_t)arg4 block:(void (^)(void))block {
kdebug_signpost_start(code, arg1, 0, 0, arg4);
block();
kdebug_signpost_end(code, arg1, 0, 0, arg4);
}
NB: The converse is a completely different issue. Queues will be affected if their target queue has a barrier. If the target queue is blocked (for example, if you change task 3 to run with a barrier on the target queue, queue1, instead), then tasks on the second queue will wait for its target queue to free up:
- (void)experiment2 {
dispatch_queue_t queue1 = dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create_with_target("2", DISPATCH_QUEUE_CONCURRENT, queue1);
dispatch_async(queue1, ^{
[self taskOnQueue:1 taskNumber:1 color:1];
});
dispatch_async(queue2, ^{
[self taskOnQueue:2 taskNumber:2 color:0];
});
dispatch_barrier_async(queue1, ^{ // changed to queue 1
[self taskOnQueue:1 taskNumber:3 color:1];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_async(queue2, ^{
[self taskOnQueue:2 taskNumber:4 color:0];
});
dispatch_async(queue1, ^{
[self taskOnQueue:1 taskNumber:5 color:1];
});
});
}
That will result in:
Here, task 3 was dispatched with a barrier (where that first Ⓢ signpost is), and not only did it not start until task 1 was done on the target queue, but task 4 (running on the second queue) on the second queue (dispatched where that second Ⓢ signpost is) waited for that barrier on its queue's target queue, too, just like task 5 did.

Related

Why asynchronous parallel queues are run at the end

I think "end" will be print in for loop, but this is wrong, can you tell me why. This is code:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (NSUInteger i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
NSLog(#"i:%lu", (unsigned long)i);
});
}
dispatch_async(queue, ^{
NSLog(#"end:%#", [NSThread currentThread]);
});
Result:
2018-03-22 19:26:33.812371+0800 MyIOSNote[96704:912772] i:990
2018-03-22 19:26:33.812671+0800 MyIOSNote[96704:912801] i:991
2018-03-22 19:26:33.812935+0800 MyIOSNote[96704:912662] i:992
2018-03-22 19:26:33.813295+0800 MyIOSNote[96704:912802] i:993
2018-03-22 19:26:33.813552+0800 MyIOSNote[96704:912766] i:994
2018-03-22 19:26:33.813856+0800 MyIOSNote[96704:912778] i:995
2018-03-22 19:26:33.814299+0800 MyIOSNote[96704:912803] i:996
2018-03-22 19:26:33.814648+0800 MyIOSNote[96704:912779] i:997
2018-03-22 19:26:33.814930+0800 MyIOSNote[96704:912759] i:998
2018-03-22 19:26:33.815361+0800 MyIOSNote[96704:912804] i:999
2018-03-22 19:26:33.815799+0800 MyIOSNote[96704:912805] end:<NSThread: 0x60400027e200>{number = 3, name = (null)}
Look at the order of execution. You first enqueue 1000 blocks to print a number. Then you enqueue the block to print "end". All of those blocks are enqueued to run asynchronously on the same concurrent background queue. All 1001 calls to dispatch_async are being done in order, one at a time, on whatever thread this code is being run on which is from a different queue all of the enqueued blocks will be run on.
The concurrent queue will pop each block in the order it was enqueued and run it. Since it is a concurrent queue and since each is to be run asynchronously, in theory, some of them could be a bit out of order. But in general, the output will appear in the same order because each block does exactly the same thing - a simple NSLog statement.
But the short answer is that "end" is printed last because it was enqueued last, after all of the other blocks have been enqueued.
What may help is to log each call as it is enqueued:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (NSUInteger i = 0; i < 1000; i++) {
NSLog(#"enqueue i: %lu", (unsigned long)i);
dispatch_async(queue, ^{
NSLog(#"run i: %lu", (unsigned long)i);
});
}
NSLog(#"enqueue end");
dispatch_async(queue, ^{
NSLog(#"run end: %#", [NSThread currentThread]);
});
For loop code runs in main Queue (Serial) so the for loop ends then the end statement is printed , if you wrapped the for loop inside the async like this
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSUInteger i = 0; i < 1000; i++) {
NSLog(#"i:%lu", (unsigned long)i);
}
});
dispatch_async(queue, ^{
NSLog(#"end:%#", [NSThread currentThread]);
});
you will get this
To combine both of the previous answers, the reason you see end printed last is because you enqueue serially, but each block executes very quickly. By the time you enqueue the log of end, all the other blocks have already executed.

Objective-C: Waiting for the last call in method

I have a method that is being called several times. But I need to act only in the very last method call. I tried dispatch_async but didn't work because still is been queue the calls:
-(void)doingSomething:(NSString*)someValue
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:1.0f];
dispatch_async(dispatch_get_main_queue(), ^{
// do something with the last call
});
});
}
Any of you knows a way to queue the calls and only use the very last call?
I'll really appreciate your help
My suggestion would be to use a dispatch_group. Call dispatch_group_enter before you call dispatch_async, and call dispatch_group_leave and the end of the block that's executed by dispatch_async. Then, after you've enqueued all the blocks, use dispatch_group_notify to schedule the completion block, which will run after all the other dispatch_async blocks have finished.
dispatch_group_t group = dispatch_group_create();
for (...) {
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
...
dispatch_group_leave(group);
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// This gets called when all the other blocks have finished
});
Alternatively, you could use an NSOperationQueue instead of libdispatch, and make a completion operation which lists every other operation as a dependency. This does have the disadvantage that the completion operation won't be executed on the main queue, though.
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// This gets called when all the other operations have finished
}];
for (...) {
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
...
}];
[completionOperation addDependency:operation];
[operationQueue addOperation:operation];
}
[operationQueue addOperation:completionOperation];

Dispatch Group not blocking code from running

I want code within a dispatch group to finish executing before anything else happens, essentially blocking the app from doing anything until this code is done. I can't get the dispatch group to block additional code from running, however. I've tried pretty much every suggestion here on stack, but I don't know what I'm doing want.
My function:
- (void)myFunction {
NSString *myString = #"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(#"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
dispatch_group_leave(group);
NSLog(#"2 we have left the dispatch group");
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"3 notifying that the dispatch group is finished");
}];
NSLog(#"4 all process are complete, we are done");
}
The output I want via log statements = 1,2,3,4
The output I get via log statements = 1,4,2,3
Why is my code skipping over the dispatch group and print 4 before 2 and 3? Any advice as to what I'm doing wrong is appreciated. Thank you!
Update:
Here is my doSomething method. My code keeps hanging on a dismiss call.
doSomething() {
viewController.dismiss(animated: false completion: { [weak self] in
doMoreCode()
})
}
Nothing here actually blocks. dispatch_group_notify just says "when the group finishes, run this." The tool you meant to use was dispatch_group_wait. If you want 1,2,3,4, then you meant this:
- (void)myFunction {
NSString *myString = #"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(#"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
NSLog(#"2 we have left the dispatch group");
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(#"3 notifying that the dispatch group is finished");
NSLog(#"4 all process are complete, we are done");
}
myFunction of course cannot be called on the main queue in iOS (since it blocks and you must never block the main queue). And it also must not be called on the same queue that doSomething:completion: uses for its completion handler (since that queue will be blocked at dispatch_group_wait).
Remember that dispatch_queue_notify just adds a block to a queue to be run sometime in the future. So it's a little unclear how you expect 3 and 4 to work (in my example I just collapsed them, but maybe you're looking for something else).
Another approach is to not block the application, and just schedule stuff to run when it's supposed to. In that case you can use the main queue. It'd look like this:
- (void)myFunction {
NSString *myString = #"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(#"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
NSLog(#"2 we have left the dispatch group");
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"3 notifying that the dispatch group is finished");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"4 all process are complete, we are done");
});
}
Note in both examples, I'm logging 2 before calling dispatch_group_leave. In this example, I'm also registering two things to be run on the main queue (in order) after the group is done. In this case, myFunction will return immediately (so it can be run on the main queue), but everything should print out in order.
Assuming doSomething runs asynchronously, you could wait for the group, but it's generally better to embrace asynchronous patterns, e.g. allow myFunction to return immediately, but supply your own completion handler to that method:
- (void)myFunctionWithCompletion:(void (^)())completion {
NSString *myString = #"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(#"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
dispatch_group_leave(group);
NSLog(#"2 we have left the dispatch group");
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"3 notifying that the dispatch group is finished");
});
}
And use it like so:
[self myFunctionWithCompletion:^{
NSLog(#"4 all process are complete, we are done");
}];
// note, it's not done when it gets here, though, because it's asynchronous
Clearly, in the above example, the dispatch group is entirely superfluous, but I assume you were doing multiple asynchronous tasks, which is why you introduced the dispatch group. If it really was just this one asynchronous task, you'd just do:
- (void)myFunctionWithCompletion:(void (^)())completion {
NSString *myString = #"Hello world";
[self doSomething:myString completion:^{
NSLog(#"The asynchronous doSomething is done");
completion();
}];
}

How to run a process in background thread iOS

I want to run a task after 6sec in background in a separate thread. I used this code for that.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self getUnsyncNamesFromServer];
}
I am not sure this run in a background thread. Do i need to use dispatch_async for this purpose. What is the best approach for this kind of situation.
dispatch_async is what you want. In the code you've used, the method inside the block will be after 6 seconds on the main queue.
For the background queue, use the follow:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
[weakSelf getUnsyncNamesFromServer];
});
For further reference, here's the GCD Apple Doc: https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_async
Your code would run in the main thread, i.e., not in background, because you are using dispatch_get_main_queue.
Instead of using the main queue, I would create a new one. The code would be something like:
dispatch_queue_t unsyncNamesQueue =
dispatch_queue_create("UnsyncNamesFromServer", DISPATCH_QUEUE_SERIAL);
//....
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC),
unsyncNamesQueue, ^{
[weakSelf getUnsyncNamesFromServer];
}
);
Be sure to read https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
This line will put your task on main thread not on separate thread
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
To put in secondary thread you have to put on global concurrent queue or make your own private queue.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC),dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //Put on global queue to run seprate thread
[self getUnsyncNamesFromServer];
}
Now to run in background(when application is in background state) you do not need to run on separate thread but if your application is taking too much time to do a task on main thread than you should put on separate thread as it is not advisable to block main thread for too much time.
Your code will not run in background state of application for that you need to register with iOS by calling beginBackgroundTaskWithExpirationHandler:
// Declare property in your class
#property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
-(void)yourfunction{
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}];
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC),dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //Put on global queue to run seprate thread
[weakSelf getUnsyncNamesFromServer];
if (weakSelf.backgroundTask != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:weakSelf.backgroundTask];
weakSelf.backgroundTask = UIBackgroundTaskInvalid;
}
});
}

queuering GCD with 2 blocks

I want create queue in that first block will run in background, then it finished I want run second block in main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// block 1
dispatch_async(dispatch_get_main_queue(), ^{
// block 2
});
});
How to add queue here?
What you have, i.e. nested GCD calls, should work fine. It should call the main thread only when the code above the GCD call to the main thread is finished.
You can make a queue like this:
dispatch_queue_t queue = dispatch_queue_create("com.company.queue", 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// block 1
dispatch_group_async(group, queue, ^{
printf("first block\n");
});
// block 2
dispatch_group_async(group, queue, ^{
printf("second block\n");
});
});
dispatch_group_notify(group, queue, ^{
printf("all tasks are finished!\n");
});
dispatch_async(dispatch_get_main_queue(), ^{
// your code on main thread to update UI
printf("main thread\n");
});

Resources