Why asynchronous parallel queues are run at the end - ios

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.

Related

How does dispatch_barrier_async interact with the target queue?

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.

main thread does dispatch_async on a concurrent queue in viewDidLoad, or within a method matters

So with some help, I am more clear on how a nested GCD works in my program.
The original post is at:
Making sure I'm explaining nested GCD correctly
However, you don't need to go through the original post, but basically the code here runs database execution in the background and the UI is responsive:
-(void)viewDidLoad {
dispatch_queue_t concurrencyQueue = dispatch_queue_create("com.epam.halo.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serialQueue = dispatch_queue_create("com.epam.halo.queue2", DISPATCH_QUEUE_SERIAL);
for ( int i = 0; i < 10; i++) {
dispatch_async(concurrencyQueue, ^() {
NSLog(#"START insertion method%d <--", i);
dispatch_sync(serialQueue, ^() {
//this is to simulate writing to database
NSLog(#"----------START %d---------", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(#"--------FINISHED %d--------", i);
});
NSLog(#"END insertion method%d <--", i);
});
}
}
However, when I start refactoring them and putting them into methods and making everything look nice, the UI does not respond anymore:
//some database singleton class
//the serial queues are declared in the class's private extension. And created in init()
-(void)executeDatabaseStuff:(int)i {
dispatch_sync(serialQueue, ^() {
//this is to simulate writing to database
NSLog(#"----------START--------- %d", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(#"--------FINISHED-------- %d", i);
});
}
-(void)testInsert:(int)i {
dispatch_async(concurrencyQueue, ^() {
[self executeDatabaseStuff:i];
});
}
//ViewController.m
- (void)viewDidLoad {
//UI is unresponsive :(
for ( int i = 0; i < totalNumberOfPortfolios; i++) {
NSLog(#"START insertion method%d <--", i);
[[DatabaseFunctions sharedDatabaseFunctions] testInsert: i];
NSLog(#"END insertion method%d <--", i);
}
}
The only way to make the refactored version work is when I put dispatch_async(dispatch_get_main_queue():
for ( int i = 0; i < totalNumberOfPortfolios; i++) {
dispatch_async(dispatch_get_main_queue(), ^() {
NSLog(#"START insertion method%d <--", i);
[[DatabaseFunctions sharedDatabaseFunctions] testInsert: i];
NSLog(#"END insertion method%d <--", i);
});
}
So my question is, I thought the fact that I use dispatch_async the concurrencyQueue will ensure that my main thread is not touched by the dispatch_sync serialQueue combo. Why is it that when I wrap it in an object/method, I must use dispatch_async(dispatch_get_main_queue()...) ?
Seems that whether my main thread does dispatch_async on a concurrent queue
in viewDidLoad, or within a method, does indeed matter.
I am thinking that the main thread is getting all these testInsert methods pushed onto its thread stack. The methods must then be processed by the main thread. Hence, even though the dispatch_sync is not blocking the main thread, the main thread runs to the end of viewDidLoad, and must wait for all the testInsert methods to be processed and done before it can move onto the next task in the Main Queue??
Notes
So I went home and tested it again with this:
for ( int i = 0; i < 80; i++) {
NSLog(#"main thread %d <-- ", i);
dispatch_async(concurrencyQueue, ^() {
[NSThread isMainThread] ? NSLog(#"its the main thread") : NSLog(#"not main thread");
NSLog(#"concurrent Q thread %i <--", i);
dispatch_sync(serialQueue, ^() {
//this is to simulate writing to database
NSLog(#"serial Q thread ----------START %d---------", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(#"serial Q thread --------FINISHED %d--------", i);
});
NSLog(#"concurrent Q thread %i -->", i);
});
NSLog(#"main thread %d --> ", i);
} //for loop
When I run the loop from 1 - 63, the UI is not blocked. And I see my database operation processing in the background.
Then when the loop is 64, UI is blocked for 1 database operation, then returns fine.
When I use 65, UI freezes for 2 database operations, then returns fine...
When I use something like 80, it gets blocked from 64-80...so I wait 16 seconds before my UI is responsive.
At the time, I couldn't figure out why 64. So now I know that its 64 concurrent threads allowed at once. ...and has nothing to do with wrapping it in a object/method. :D
Many thanks for the awesome help from the contributors!
There is a hard limit of 64 GCD concurrent operations (per top level concurrent queue) that can be run together.
What's happening is you're submitting over 64 blocks to your concurrent queue, each of them getting blocked by the [NSThread sleepForTimeInterval:1.0f], forcing a new thread to be created for each operation. Therefore, once the thread limit is reached, it backs up and starts to block the main thread.
I have tested this with 100 "database write" operations (on device), and the main thread appears to be blocked until 36 operations have taken place (there are now only 64 operations left, therefore the main thread is now un-blocked).
The use of a singleton shouldn't cause you any problems, as you're calling methods to that synchronously, therefore there shouldn't be thread conflicts.
The simplest solution to this is just to use a single background serial queue for your "database write" operations. This way, only one thread is being created to handle the operation.
- (void)viewDidLoad {
[super viewDidLoad];
static dispatch_once_t t;
dispatch_once(&t, ^{
serialQueue = dispatch_queue_create("com.epam.halo.queue2", DISPATCH_QUEUE_SERIAL);
});
for (int i = 0; i < 100; i++) {
[self testInsert:i];
}
}
-(void)executeDatabaseStuff:(int)i {
//this is to simulate writing to database
NSLog(#"----------START--------- %d", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(#"--------FINISHED-------- %d", i);
}
-(void)testInsert:(int)i {
NSLog(#"Start insert.... %d", i);
dispatch_async(serialQueue, ^() {
[self executeDatabaseStuff:i];
});
NSLog(#"End insert... %d", i);
}
I don't know why inserting dispatch_async(dispatch_get_main_queue(), ^() {} inside your for loop was working for you... I can only assume it was off-loading the "database writing" until after the interface had loaded.
Further resources on threads & GCD
Number of threads created by GCD?
https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW2

ObjectiveC waiting on for loop blocks with semaphore

I have to run a method with block, several times inside a for loop.
I also have to wait until all the blocks execution completes.
My problem is that I can't understand what I do wrong, that causes my entire app to freeze. Here is the code:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//1 - creating semaphore
for(int i = 0; i< myObj.count; i++){
[[DataManager shared] verifyObjectId:myObj[i].id
completionBlock:^(BOOL found) {
if(found){
//code here
dispatch_semaphore_signal(semaphore);//3 - signaling semaphore to continue
}
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//2 - getting semaphore to wait
}
//I want to continue once all DB checks complete
Now, I don't understand, why the semaphore won't release, and the for loop won't continue.
What I actually need, is for the semaphore to release after all the DB checks complete. Ideally, I would want the semaphore to wait outside the for loop. Any suggestions on how to accomplish this?
EDIT: SOLUTION: (based on the accepted answer)
// create a group
dispatch_group_t group = dispatch_group_create();
for(int i = 0; i< myObj.count; i++){
// pair a dispatch_group_enter for each dispatch_group_leave
dispatch_group_enter(group);
[[DataManager shared] verifyObjectId:myObj[i].id
completionBlock:^(BOOL found) {
if(found){
//code here
}
dispatch_group_leave(group); //1 leave
}];
//Get a notification on a block that will be scheduled on the specified queue
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(#"-all done!-");
//code here
});
}
Without access to verifyObjectid:completiongBlock:, there are a couple of issues. First, you only call dispatch_semaphore_signal if found is true. If found is every false, you'll deadlock. That may just be a transcription error and your real code might not do that.
Another guess is that the completion block is being submitted to the queue that you're currently running on (the main queue?) If that's true, then that would definitely be a deadlock, because you'll never run dispatch_semaphore_signal since it's waiting on dispatch_semaphore_wait. I can't tell without information about DataManager.
Your approach also serializes the calls, whereas I think you wanted them to be in parallel. Each call has to wait for the former one to finish in your code.
The better tools to use here are dispatch_apply and dispatch_group. Something like this (untested):
dispatch_group_t group = dispatch_group_create();
dispatch_apply(myObj.count, dispatch_get_global_queue(0, 0), ^(size_t i){
dispatch_group_enter(group);
[[DataManager shared] verifyObjectId:myObj[i].id
completionBlock:^(BOOL found) {
if(found){
//code here
}
dispatch_group_leave(group));
}];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_apply won't return until all the blocks have completed running, which means that dispatch_group_enter has run "count" times. You then use dispatch_group_wait to wait for all the calls to dispatch_group_leave.

Waiting for nested AFNetworking calls and for loops to finish

So, I've been searching all over but didn't find a solution, or at least I couldn't apply it.
I've found this thread here on stackoverflow, but didn't succeed in implementing it in my code.
My issue is, that I need to know when nested AFNetworking calls and For loops are done. I've tried it with GCD groups, but with no luck.
The code looks like this:
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_async(queue, ^{
[[JSON GET method using AFNetworking 2.0] success:^(NSArray *result) {
dispatch_group_async(group, queue, ^{
//do some work with the result
for (NSDictionary *resultPartDictionary in result) {
dispatch_group_async(group, queue, ^{
//do some more work with parts of the result
[[JSON GET method based on result] success:^(NSArray *result) {
dispatch_group_async(group, queue, ^{
//do some work
for (NSDictionary *resultPartDictionary in result) {
dispatch_group_async(group, queue, ^{
//do some work
[[JSON GET method based on result] success:^(NSArray *result) {
dispatch_group_async(group, queue, ^{
//do some work
for (NSDictionary *resultPartDictionary in result) {
dispatch_group_async(group, queue, ^{
//do some work
});
}
});
}];
});
}
});
}];
});
}
});
}
});
}
Right now, everything works. I'm handling Core Data inside the blocks, so I needed MOCs for every thread, which works as well.
The only thing I'd like to know is how to know when all these blocks finish.
Thank you!
EDIT
So, I've tried using dispatch_group_enter(group) and dispatch_group_leave(group), but it seems to me, that it's just not possible with this embedded architecture. Because of the For loops, the "leave" notifications are either too many, which causes an exception or not enough and the dispatch_group_notify returns too early.
Any ideas on this?
You are looking for dispatch_group_notify and dispatch_group_enter/dispatch_group_leave.
dispatch_group_notify executes the given block in the given queue, when every block in the group is finished.
dispatch_group_enter increases the current count of executing tasks in the group. Every dispatch_group_enter must be balanced with a call to dispatch_group_leave.
dispatch_group_leave decreases the current count of executing tasks in the group.
So, you should trick dispatch_group_notify with increase the number of the tasks in the group before your network calls start and decrease it when everything finished. To achieve this, call dispatch_group_enter before dispatch_async and call dispatch_group_leave in the last thread. Since you know the element count of the every result array, you can check if the current thread is the last one.
dispatch_group_enter(group); // Increases the number of blocks in the group.
dispatch_async(queue, ^{
// Make your AFNetworking calls.
dispatch_group_async(group, queue, ^{
//do some work.
if (isLastThread)
dispatch_group_leave(group); // Decreases the number of blocks in the group.
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // Calls the given block when all blocks are finished in the group.
// All blocks finished, do whatever you like.
});

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