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.
Related
I have to protect a critical section of my code.
I don't want the caller to be blocked by the function that can be time consuming so I'm creating a serial queue with background qos and then dispatching asynchronously:
private let someQueue = DispatchQueue(label: "\(type(of: self)).someQueue", qos: .background)
func doSomething() {
self.someQueue.async {
//critical section
}
}
For my understanding, the function will directly return on the calling thread without blocking.
I've also seen somewhere dispatching first asynchronously on the global queue, the synchronously on a serial queue:
private let someQueue2 = DispatchQueue(label: "\(type(of: self)).someQueue2")
func doSomething() {
DispatchQueue.global(qos: .background).async {
self.someQueue2.sync {
//critical section
}
}
}
What's the difference between the two approaches?
Which is the right approach?
In the first approach, the calling thread is not blocked and the task (critical section) passed in the async block will be executed in background.
In the second approach, the calling thread is not blocked, but the "background" thread will be waiting for the sync block (critical section) execution which is executed by another thread.
I don't know what you do in your critical section, but it seems first approach seems the best one. Note that background qos is quite slow, maybe use default qos for your queue, unless you know what you are doing. Also note that convention wants that you use bundle identifier as label for your queue. So something like this:
private let someQueue = DispatchQueue(label: "\(Bundle.main.bundleIdentifier ?? "").\(type(of: self)).someQueue")
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.
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()
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
}
}
}