Why is the below code running in main thread although i have specified qos to background thread?
func testQueue(){
let queue = DispatchQueue(label: "com.appcoda.myqueue",qos:.background)
queue.sync {
if Thread.isMainThread{
print("is in main thread")
}else{
print("is i background thread")
}
for i in 100..<110 {
print("Ⓜ️", i)
}
}
}
testQueue()
Whenever i try to run the method i get msg in console as is in main threadwhich should not be the case..I am reading in this article.
http://www.appcoda.com/grand-central-dispatch/
See the Getting Started with dispatch queues section.
You have specified a background queue, not a background thread. When you dispatch a task on a queue, GCD looks for a thread to run the task on.
Since you are using a synchronous dispatch, the main queue is blocked, leaving the main thread free to perform work, so your task executes on the main thread.
tasks dispatched on the main queue will run on the main thread
tasks dispatched on another queue may run on the main thread or another thread
Here is the Solution :-
Default for main thread is always sync, so no matter what queue you have created with what properties, it will always run in main thread when it is declared as sync.
Refer this Image
Second thing Main thread can run in async as well and it will guarantees that this new task will execute sometime after the current method finishes.
Third thing is when you will try the same code with async then it will work fine.
Refer this image
See the following code and figure it out :-
func testQueue(){
let queue = DispatchQueue(label: "com.appcoda.myqueue",qos:.background, attributes:DispatchQueue.Attributes.concurrent)
queue.sync { //Sync will always create main thread
if Thread.isMainThread{
print("is in main thread")
}else{
print("is i background thread")
}
}
DispatchQueue.main.async { //Main thread can be async
if Thread.isMainThread{
print("is in main thread")
}else{
print("is i background thread")
}
}
if Thread.isMainThread { //default is Main thread sync
for i in 100..<110 {
print("Ⓜ️", i)
}
}
else{
print("is i background thread")
}
queue.async { //Here your custom async thread will work
if Thread.isMainThread{
print("is in main thread")
}else{
print("is i background thread")
}
}
}
testQueue()
Related
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.
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
Which thread is blocked by Swift's sleep: method?
let customConcurrentQueue = DispatchQueue(label: "CustomConcurrentQueue", attributes: .concurrent)
customConcurrentQueue.async {
sleep(5)
print("1")
}
print("2")
Will sleep method block the Main thread also?
No, the main thread will not be blocked.
It is because you are calling an async block on the customConcurrentQueue, which allows the main thread to continue running immediately. If you used sync instead, the main thread would wait until the sync block is finished running.
I am curious if read / write operations like this are executed on the main thread:
try! realm.write {
realm.add(myDog)
}
This is important because I want to run operations directly after something has been read or written to realm.
Block is executed on the same thread as the thread that calls the write() method synchronously. In different words, if you call write() on the main thread, the block will be executed on the main thread.
dispatch_async(dispatch_queue_create("background", nil)) {
// Some operations in a background thread ...
try! realm.write {
// this block will be executed on the background thread
}
}
If you'd like to execute the write operation on the main thread, you may need to dispatch to the main thread as needed.
dispatch_async(dispatch_queue_create("background", nil)) {
// Some operations in a background thread ...
dispatch_async(dispatch_get_main_queue()) {
try! realm.write {
// this block will be executed on the main thread
}
}
}