so what I understood from basics is:
the async task will not block the current queue, sync task will block the current queue until that task finishes.
but when I run code on the main queue, just to print number from 1 to 100, it is giving me the same output.
use case -1
DispatchQueue.global(qos: .default).async {
DispatchQueue.main.async {
for i in 0...100{
print(i)
sleep(arc4random() % 5)
}
}
}
==============
use case -2
DispatchQueue.global(qos: .default).async {
DispatchQueue.main.sync {
for i in 0...100{
print(i)
sleep(arc4random() % 5)
}
}
what can be the reason for getting the same output for async vs sync on the main queue?
Is it because the serial queue will have only one thread associated with it?
When you, a queue, say DispatchQueue.main.sync or DispatchQueue.main.async, that does not affect how the main queue behaves; it affects how you behave, ie whether you wait before doing what comes after the call. But you have nothing after the call so there is no distinction to draw. Your “test” does not test anything.
Related
I have a method which gives me photo auth status.
func photosAuthorizationStatus() -> PHAuthorizationStatus {
var authStatus = PHAuthorizationStatus.notDetermined
let semaphore = DispatchSemaphore(value: 0)
PHPhotoLibrary.requestAuthorization { (status: PHAuthorizationStatus) in
authStatus = status
semaphore.signal()
}
semaphore.wait()
return authStatus
}
I am calling this method in viewDidAppear of a ViewController , but Application is not freezing.
But if I call semaphore.wait when I ask mainQueue explicitly Application is freezing.
DispatchQueue.main.async{
let semaphore = DispatchSemaphore(value: 0)
semaphore.wait()
}
// Above code will freeze the application.
Can I know the reason ?
In your title, you ask:
can I call on semaphore.wait() main thread?
You should avoid blocking the main thread for any reason. Whether wait for semaphores or dispatch groups, or even synchronously dispatching (e.g., sync) of anything more than a few milliseconds. You risk having the watchdog process kill your app if you do this at the wrong time, and it results in a horrible UX.
You then go on to ask:
DispatchQueue.main.async {
let semaphore = DispatchSemaphore(value: 0)
semaphore.wait()
}
Above code will freeze the application.
Can I know the reason
That code says “block the main thread waiting for a signal on this semaphore”. So, until that signal arrives, the main thread will be blocked. But the main thread should never be blocked because it services, amongst other things, the UI, and your app will freeze if you deadlock the main thread.
Bottom line, never block the main thread.
Create completion closure in method which will call after successful request authorisation completed. See following code.
Make sure your have added permission key "Privacy - Photo Library Usage Description" in Info.plist file.
func photosAuthorizationStatus(completion: #escaping (PHAuthorizationStatus) -> Void) {
PHPhotoLibrary.requestAuthorization { (status: PHAuthorizationStatus) in
completion(status)
}
}
Use:
self.photosAuthorizationStatus { (status) in
// use your status here
}
Output:
A bit late to the party, but it may be useful for others, since nobody explained the real issue here.
Calling semaphore.wait() decreases the counting semaphore. If the counter becomes smaller than zero, wait() blocks the main queue until you signal the semaphore.
Now, you invoke semaphore.signal() in the completion closure, which happens to execute on the main queue. But the main queue is blocked, so it won't call semaphore.signal(). wait() and signal() will wait for each other for eternity -> a guaranteed classic deadlock!
Forget the semaphore, and refactor the photosAuthorizationStatus() method to return the result via a closure, as suggested by Sagar Chauhan.
So I have this code in a Swift 4 playground:
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
DispatchQueue.main.async {
for _ in 1...5 { print("Main") }
}
DispatchQueue.global().async {
for _ in 1...5 { print("Background") }
}
DispatchQueue.global(qos: .userInteractive).async {
for _ in 1...5 { print("User Interactive") }
}
Why does this print out:
User Interactive
Background
Background
User Interactive
User Interactive
Background
User Interactive
Background
User Interactive
Background
Main
Main
Main
Main
Main
I understand why the "User Interactive" and "Background" are printing out in a random order, but why is "Main" printing out after? Shouldn't it be prioritized because it's in the main queue? I guess I still don't fully understand how GCD and QOS works in Swift 4.
EDIT
I now added these two print outs and a few lines:
print("BEGINNING OF CODE")
DispatchQueue.main.async {
for _ in 1...5 { print("Main") }
}
DispatchQueue.global().sync {
for _ in 1...5 { print("Sync Global") }
}
DispatchQueue.global(qos: .background).async {
for _ in 1...5 { print("Background") }
}
DispatchQueue.global(qos: .userInteractive).async {
for _ in 1...5 { print("User Interactive") }
}
print("END OF CODE")
And it prints this out:
BEGINNING OF CODE
Sync Global
Sync Global
Sync Global
Sync Global
Sync Global
END OF CODE
User Interactive
Background
User Interactive
User Interactive
User Interactive
User Interactive
Background
Background
Background
Background
Main
Main
Main
Main
Main
My question now is: shouldn't the background queues be printed out before the "END OF CODE"? If the last two blocks were enqueued and immediately started executing, why aren't they printing out before? Is it because the main queue has a higher priority? But shouldn't it run asynchronously?
The code in the playground is being run on the main queue. So the first for loop is enqueued on the main queue and it won't be run until the end of your code is reached.
The other two blocks are enqueued onto background queues and can start running immediately.
The code you have running on the background queues is so simple and quick that it happens to finish before the first block has a chance to run.
Add a sleep inside one of the two background blocks and you will see that the "Main" output comes out sooner. Your test gives misleading results because it is to simple and too fast.
Global queues are non FIFO type.. They occupy a thread soon as they find one that is available. Thats why they run faster..
The main queue task runs serially on main thread.. usually called from background when UI needs updation
(Perhaps answered by How does a serial dispatch queue guarantee resource protection? but I don't understand how)
Question
How does gcd know when an asynchronous task (e.g. network task) is finished? Should I be using dispatch_retain and dispatch_release for this purpose? Update: I cannot call either of these methods with ARC... What do?
Details
I am interacting with a 3rd party library that does a lot of network access. I have created a wrapper via a small class that basically offers all the methods i need from the 3rd party class, but wraps the calls in dispatch_async(serialQueue) { () -> Void in (where serialQueue is a member of my wrapper class).
I am trying to ensure that each call to the underlying library finishes before the next begins (somehow that's not already implemented in the library).
The serialisation of work on a serial dispatch queue is at the unit of work that is directly submitted to the queue. Once execution reaches the end of the submitted closure (or it returns) then the next unit of work on the queue can be executed.
Importantly, any other asynchronous tasks that may have been started by the closure may still be running (or may not have even started running yet), but they are not considered.
For example, for the following code:
dispatch_async(serialQueue) {
print("Start")
dispatch_async(backgroundQueue) {
functionThatTakes10Seconds()
print("10 seconds later")
}
print("Done 1st")
}
dispatch_async(serialQueue) {
print("Start")
dispatch_async(backgroundQueue) {
functionThatTakes10Seconds()
print("10 seconds later")
}
print("Done 2nd")
}
The output would be something like:
Start
Done 1st
Start
Done 2nd
10 seconds later
10 seconds later
Note that the first 10 second task hasn't completed before the second serial task is dispatched. Now, compare:
dispatch_async(serialQueue) {
print("Start")
dispatch_sync(backgroundQueue) {
functionThatTakes10Seconds()
print("10 seconds later")
}
print("Done 1st")
}
dispatch_async(serialQueue) {
print("Start")
dispatch_sync(backgroundQueue) {
functionThatTakes10Seconds()
print("10 seconds later")
}
print("Done 2nd")
}
The output would be something like:
Start
10 seconds later
Done 1st
Start
10 seconds later
Done 2nd
Note that this time because the 10 second task was dispatched synchronously the serial queue was blocked and the second task didn't start until the first had completed.
In your case, there is a very good chance that the operations you are wrapping are going to dispatch asynchronous tasks themselves (since that is the nature of network operations), so a serial dispatch queue on its own is not enough.
You can use a DispatchGroup to block your serial dispatch queue.
dispatch_async(serialQueue) {
let dg = dispatch_group_create()
dispatch_group_enter(dg)
print("Start")
dispatch_async(backgroundQueue) {
functionThatTakes10Seconds()
print("10 seconds later")
dispatch_group_leave(dg)
}
dispatch_group_wait(dg)
print("Done")
}
This will output
Start
10 seconds later
Done
The dg.wait() blocks the serial queue until the number of dg.leave calls matches the number of dg.enter calls. If you use this technique then you need to be careful to ensure that all possible completion paths for your wrapped operation call dg.leave. There are also variations on dg.wait() that take a timeout parameter.
As mentioned before, DispatchGroup is a very good mechanism for that.
You can use it for synchronous tasks:
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
syncTask()
}
group.notify(queue: .main) {
// done
}
It is better to use notify than wait, as wait does block the current thread, so it is safe on non-main threads.
You can also use it to perform async tasks:
let group = DispatchGroup()
group.enter()
asyncTask {
group.leave()
}
group.notify(queue: .main) {
// done
}
Or you can even perform any number of parallel tasks of any synchronicity:
let group = DispatchGroup()
group.enter()
asyncTask1 {
group.leave()
}
group.enter() //other way of doing a task with synchronous API
DispatchQueue.global().async {
syncTask1()
group.leave()
}
group.enter()
asyncTask2 {
group.leave()
}
DispatchQueue.global().async(group: group) {
syncTask2()
}
group.notify(queue: .main) {
// runs when all tasks are done
}
It is important to note a few things.
Always check if your asynchronous functions call the completion callback, sometimes third party libraries forget about that, or cases when your self is weak and nobody bothered to check if the body got evaluated when self is nil. If you don't check it then you can potentially hang and never get the notify callback.
Remember to perform all the needed group.enter() and group.async(group: group) calls before you call the group.notify. Otherwise you can get a race condition, and the group.notify block can fire, before you actually finish your tasks.
BAD EXAMPLE
let group = DispatchGroup()
DispatchQueue.global().async {
group.enter()
syncTask1()
group.leave()
}
group.notify(queue: .main) {
// Can run before syncTask1 completes - DON'T DO THIS
}
The answer to the question in your questions body:
I am trying to ensure that each call to the underlying library finishes before the next begins
A serial queue does guarantee that the tasks are progressed in the order you add them to the queue.
I do not really understand the question in the title though:
How does a serial queue ... know when a task is complete?
I have the following code:
func testFunc(completion: (Bool) -> Void) {
let queue = NSOperationQueue()
queue.maxConcurrentOperationCount = 1
for i in 1...3 {
queue.addOperationWithBlock{
Alamofire.request(.GET, "https://httpbin.org/get").responseJSON { response in
switch (response.result){
case .Failure:
print("error")
break;
case .Success:
print("i = \(i)")
}
}
}
//queue.addOperationAfterLast(operation)
}
queue.waitUntilAllOperationsAreFinished()
print("finished")
}
and output is:
finished
i = 3
i = 1
i = 2
but I expect the following:
i = 3
i = 1
i = 2
finished
So, why queue.waitUntilAllOperationsAreFinished() don't wait?
Each operation you've added into queue is immediately executed because Alamofire.request simply returns without waiting for the response data.
Furthermore, there is a possibility of deadlock there. Since responseJSON block is executed within the main queue by default, blocking the main thread by calling waitUntilAllOperationsAreFinished will prevent it from executing the completion block at all.
First, in order to fix the deadlock issue, you can tell Alamofire to execute the completion block in a different queue, second, you can use dispatch_group_t to group the number of asynchronous HTTP requests and keep the main thread waiting till all those requests in the group finish executing:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
let group = dispatch_group_create()
for i in 1...3 {
dispatch_group_enter(group)
Alamofire.request(.GET, "https://httpbin.org/get").responseJSON(queue: queue, options: .AllowFragments) { response in
print(i)
dispatch_async(dispatch_get_main_queue()) {
// Main thread is still blocked. You can update the UI here but it will take effect after all HTTP requests are finished.
}
dispatch_group_leave(group)
}
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
print("finished")
I would suggest you to use KVO and observe when the queue has finish all the task instead of blocking the current thread until all the operations finished. Or you can use dependencies. Take a look at this SO question
To check whether all operations finished - We could use KVO to observe number of operations in the Queue. Unfortunately both operations and operationCount are currently deprecated..!
So it's safe to use following option using dependency.
To check few operations are finished - Use Dependencies :
Create a final operation called "finishOperation" then add dependencies to all other required operation. This way, "finishOperation" will be executed only when depended operations are finished. Check this answer for code sample.
I am trying to get the hang of Grand Central Dispatch to make improvements to the overall efficiency of my code, however, I am not entirely certain of how to use it. Below are examples of my current (possibly horribly mislead) assumptions on GCD, based on a tutorial as well as numerous other stack overflow questions, a few swift blogs, and some other random websites....
dispatch_async(dispatch_get_main_queue()){
//this will execute the task wihtin this closure, asynchonously, meaning
//that it does not block the thread, yet the code within the closure will
//be executed serailly, meaning each line must finish before proceeding
print(1)
print(2)
print(3)
print(4)
//this will print 1,2,3,4, in that exact order, everytime. But since it is
//called asynchronously it returns immediatley after initiating the task, and
//thus does not block the UI from being interacted with
}
dispatch_sync(dispatch_get_main_queue()){
//this will execute the taks within the closure synchronously, meaning that it WILL block
//the current thread until the task is completed at which point it will return and make the
//thread available again. The main queue is a serial queue, so each line will be started
//and finish before the line after it is called.
print(1)
print(2)
print(3)
print(4)
//this will print 1,2,3,4, in that exact order, everytime. Because it is called synchronously
//and on the main queue, it will block the UI. The UI will not become interactive again
//until this function returns, and it will not return until "4" is printed.
}
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.rawValue), 0)){
//this will execute the task within the closure asycnrhonously, meaning that
//it does not block the current thread and will return before completion of all its contents.
//As well, this is executed on a concurrent queue, so the lines within will begin
//in the order they are shown however they may finish at any time
print(1)
print(2)
print(3)
print(4)
//this could print the 4 numbers in ANY ORDER. Since it is called asynchronously it returns immediatley,
//and does not block the current thread. And because the global_queue is a
//concurrent queue, the numbers could print in any order
}
dispatch_sync(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.rawValue), 0)){
//this will execute the tasks within synchronously, meaning that it WILL block the
//current thread until the task is completed. It is dispatched to a concurrent queue,
//therefore the tasks within will each begin in the order that they are read,
//however they may finish at any time
print(1)
print(2)
print(3)
print(4)
//this could print 1,2,3,4 in ANY ORDER. This WILL block the current thread since
//it is called synchronously. However, since it is being called on a concurrent queue,
//it could print the numbers in any order.
}
Are my assumptions correct? If not can you tell me where I am wrong and why? Thank you very much if you have taken the time to read this.
You got almost everything correct, with this notable exception:
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.rawValue), 0)){
/*
...
this is executed on a concurrent queue, so the lines within will begin
in the order they are shown however they may finish at any time
...
this could print the 4 numbers in ANY ORDER.
...
because the global_queue is a
concurrent queue, the numbers could print in any order
*/
}
Your understanding of the asynchronous aspect is correct; the async calls return immediately without blocking the thread they are called from. However, the concurrency of the dispatched routine is not in regards to amongst instructions within that routine. Note that closures are essentially just functions which capture their scope, so there is no unique behavior involved with the execution of their contents.
Instead, the concurrency occurs amongst multiple routines (i.e. multiple closures) dispatched to the concurrent queue. Take the following:
// These routines are executed asynchronously to the current thread, and
// serially to one another; the results will always be 1 through 4.
let serialQueue = dispatch_get_main_queue()
dispatch_async(serialQueue) { print(1) }
dispatch_async(serialQueue) { print(2) }
dispatch_async(serialQueue) { print(3) }
dispatch_async(serialQueue) { print(4) }
// These routines are executed asynchronously to the current thread, and
// concurrently to one another; they will print out in an undefined order.
let concurrentQueue = dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.rawValue), 0)
dispatch_async(concurrentQueue) { print(1) }
dispatch_async(concurrentQueue) { print(2) }
dispatch_async(concurrentQueue) { print(3) }
dispatch_async(concurrentQueue) { print(4) }