Why Dispatch_group_notify works different in different environment? - ios

The first situation is that I create a Command Line Tool Application,and run this code.
NSLog(#"Main:%#", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(#"Task1:%#", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(#"Task2:%#", [NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"Finish:%#", [NSThread currentThread]);
});
The log in terminal is
Main:<NSThread: 0x1028033b0>{number = 1, name = main}
Task2:<NSThread: 0x10040f0f0>{number = 2, name = (null)}
Task1:<NSThread: 0x1006008d0>{number = 3, name = (null)}
If I want to show last log in queue and replace the main queue
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"Finish:%#", [NSThread currentThread]);
});
with queue
dispatch_group_notify(group, queue, ^{
NSLog(#"Finish:%#", [NSThread currentThread]);
});
The terminal print the last log.But why it can't revoke in Main queue?
When i copy this code to simple iOS Application.All works well:
Main:<NSThread: 0x600000070ac0>{number = 1, name = main}
Task2:<NSThread: 0x6000002633c0>{number = 3, name = (null)}
Task1:<NSThread: 0x600000263480>{number = 4, name = (null)}
MainFinish:<NSThread: 0x600000070ac0>{number = 1, name = main}
And I try to add sleep(1) over Task1 in 'Command Tool Line', but it seems block the queue and only print log:Task2.... But this all works well in simple iOS Application.
Why lead to these different?

image here
Unlike other queues that active on created,you should call dispatch_main() method to execute blocks submitted to main thread
image here
The reason for the same code runs well in iOS Application is that the application start a default runloop which execute the task submitted to the main queue according to a certain rule like UI update notification.
The reference as follow:
swift-corelibs-libdispatch
Concurrency Programming Guide

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.

Concurrency on NSMainQueueConcurrencyType and NSPrivateQueueConcurrencyType contexts?

Managed object context initialised with NSMainQueueConcurrencyType and NSPrivateQueueConcurrencyType ties to main queue and private queue which are serial queues where operations are executed in FIFO order.
With below sample code:
NSLog(#"Current thread : %#", [NSThread currentThread]);
[mainMoc performBlock:^{
NSLog(#"main 1 - %#", [NSThread currentThread]);
}];
[mainMoc performBlockAndWait:^{
NSLog(#"main 2 - %#", [NSThread currentThread]);
}];
[mainMoc performBlock:^{
NSLog(#"main 3 - %#", [NSThread currentThread]);
}];
[bgMoc performBlock:^{
NSLog(#"bg 1 - %#", [NSThread currentThread]);
}];
[bgMoc performBlockAndWait:^{
NSLog(#"bg 2 - %#", [NSThread currentThread]);
}];
[bgMoc performBlock:^{
NSLog(#"bg 3 - %#", [NSThread currentThread]);
}];
I was expecting it to print
main 1, main 2 and main 3 like bg 1, bg 2 and bg 3 in serial order but instead this was printed:
Current thread : <NSThread: 0x60000006fb80>{number = 1, name = main}
main 2 - <NSThread: 0x60000006fb80>{number = 1, name = main}
bg 1 - <NSThread: 0x600000268900>{number = 3, name = (null)}
bg 2 - <NSThread: 0x60000006fb80>{number = 1, name = main}
bg 3 - <NSThread: 0x600000268900>{number = 3, name = (null)}
main 1 - <NSThread: 0x60000006fb80>{number = 1, name = main}
main 3 - <NSThread: 0x60000006fb80>{number = 1, name = main}
What could be the theory behind it given both main and private queue are serial?
Concurrency is non-deterministic. The only thing you're guaranteed is that "main1" is being executed before "main3", because it is, as you said, a FIFO queue.
It is important to differentiate between performBlock and performBlockAndWait.
performBlock is asynchronous, so it just puts the block into the queue and returns immediately. Those blocks will be executed in order. Thats why "main1" will always be executed before "main3".
performBlockAndWait is synchronous and thus can't take care of the queue by definition. This means it will execute the block right now and it won't return until it's done.
If it didn't do this, it would block, because the queue isn't empty or it would have to execute all the other tasks in the queue first.
Now the reason for why "bg1" comes before "bg2" is scheduling. I'm almost certain, that if you execute this test multiple times, it might eventually be different.
If the main-thread would be faster to reach the synchronous "bg2" it would appear first.

GCD sync serial main_queue [duplicate]

This question already has answers here:
Why dispatch_sync( ) call on main queue is blocking the main queue?
(5 answers)
dispatch_sync() always execute block in main thread
(3 answers)
Closed 6 years ago.
-(void)test1{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(#"start");
dispatch_sync(queue, ^{
NSLog(#"%#",[NSThread currentThread]);
});
}
-(void)test2{
dispatch_queue_t queue = dispatch_queue_create("com.yaoye.serial", DISPATCH_QUEUE_SERIAL);
NSLog(#"start");
dispatch_sync(queue, ^{
NSLog(#"%#",[NSThread currentThread]);
});
}
Test1 and test2 are executed in the main thread
Test1 example:
the main thread is blocked waiting for synchronization function, block into the main thread of the runloop cannot be executed, lead to deadlock.
Test2 example:
the main thread waiting for synchronization function is blocked,block into the main thread of the runloop, but no deadlock.<2016-03-14 13:55:06.730 GCD[54320:12111593] <NSThread: 0x7fef4ac08810>{number = 1, name = main}>
queation:
Why not is test2 deadlock?
because test1 uses the same main queue, whereas test2 uses a different queue

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