Understanding dispatch_async
Asked Answered
A

3

248

I have question around this code

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData* data = [NSData dataWithContentsOfURL: 
      kLatestKivaLoansURL];
    [self performSelectorOnMainThread:@selector(fetchedData:) 
      withObject:data waitUntilDone:YES];
});

The first parameter of this code is

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Are we asking this code to perform serial tasks on global queue whose definition itself is that it returns global concurrent queue of a given priority level?

What is advantage of using dispatch_get_global_queue over the main queue?

I am confused. Could you please help me to understand this better.

Absinthe answered 29/4, 2013 at 16:27 Comment(1)
You should better cut your code in several lines so its making more sense. safe your dispatch_get_global_queue inside a variable type of dispatch_queue_t myQueue. Its more readable passing only myQueue to your ``dispatch_async`Bottomless
B
541

The main reason you use the default queue over the main queue is to run tasks in the background.

For instance, if I am downloading a file from the internet and I want to update the user on the progress of the download, I will run the download in the priority default queue and update the UI in the main queue asynchronously.

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});
Bankbook answered 29/4, 2013 at 16:31 Comment(7)
I understand that david thanks for your answer that but my question was more around to understand logic of doing this i.e are asking this code to perform serial tasks on global queue which is concurrent queue itselfAbsinthe
I"m doing exactly what you suggest but somehow, the uiTableViewCell doesn'tupdate right away when I call [self.tableView reloadData] in the Run UI Updates. It takes about 4 or 5 seconds. It's been driving me crazy for several days now.Intersexual
@Intersexual I'm not too familiar with that method. Maybe that method just takes 5 seconds to run. The important thing with dispatch_async is that it allows you to do things in the background without hanging the main thread.Bankbook
@GrandSteph, usually that means your UITableView DataSource or Delegate Methods takes too long, since once you called "reloadData", DataSource and some Delegate functions will be called before the view updated. Sometimes, if you have some heavy computation if those functions, it will take longBulbul
what does the 0 signify?Tyronetyrosinase
@Honey The 0 is the flags parameter, which currently does absolutely nothing. From the docs: Flags that are reserved for future use. Always specify 0 for this parameter.Bankbook
what does ^(){ mean?Afterglow
C
201

All of the DISPATCH_QUEUE_PRIORITY_X queues are concurrent queues (meaning they can execute multiple tasks at once), and are FIFO in the sense that tasks within a given queue will begin executing using "first in, first out" order. This is in comparison to the main queue (from dispatch_get_main_queue()), which is a serial queue (tasks will begin executing and finish executing in the order in which they are received).

So, if you send 1000 dispatch_async() blocks to DISPATCH_QUEUE_PRIORITY_DEFAULT, those tasks will start executing in the order you sent them into the queue. Likewise for the HIGH, LOW, and BACKGROUND queues. Anything you send into any of these queues is executed in the background on alternate threads, away from your main application thread. Therefore, these queues are suitable for executing tasks such as background downloading, compression, computation, etc.

Note that the order of execution is FIFO on a per-queue basis. So if you send 1000 dispatch_async() tasks to the four different concurrent queues, evenly splitting them and sending them to BACKGROUND, LOW, DEFAULT and HIGH in order (ie you schedule the last 250 tasks on the HIGH queue), it's very likely that the first tasks you see starting will be on that HIGH queue as the system has taken your implication that those tasks need to get to the CPU as quickly as possible.

Note also that I say "will begin executing in order", but keep in mind that as concurrent queues things won't necessarily FINISH executing in order depending on length of time for each task.

As per Apple:

https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

A concurrent dispatch queue is useful when you have multiple tasks that can run in parallel. A concurrent queue is still a queue in that it dequeues tasks in a first-in, first-out order; however, a concurrent queue may dequeue additional tasks before any previous tasks finish. The actual number of tasks executed by a concurrent queue at any given moment is variable and can change dynamically as conditions in your application change. Many factors affect the number of tasks executed by the concurrent queues, including the number of available cores, the amount of work being done by other processes, and the number and priority of tasks in other serial dispatch queues.

Basically, if you send those 1000 dispatch_async() blocks to a DEFAULT, HIGH, LOW, or BACKGROUND queue they will all start executing in the order you send them. However, shorter tasks may finish before longer ones. Reasons behind this are if there are available CPU cores or if the current queue tasks are performing computationally non-intensive work (thus making the system think it can dispatch additional tasks in parallel regardless of core count).

The level of concurrency is handled entirely by the system and is based on system load and other internally determined factors. This is the beauty of Grand Central Dispatch (the dispatch_async() system) - you just make your work units as code blocks, set a priority for them (based on the queue you choose) and let the system handle the rest.

So to answer your above question: you are partially correct. You are "asking that code" to perform concurrent tasks on a global concurrent queue at the specified priority level. The code in the block will execute in the background and any additional (similar) code will execute potentially in parallel depending on the system's assessment of available resources.

The "main" queue on the other hand (from dispatch_get_main_queue()) is a serial queue (not concurrent). Tasks sent to the main queue will always execute in order and will always finish in order. These tasks will also be executed on the UI Thread so it's suitable for updating your UI with progress messages, completion notifications, etc.

Contend answered 3/7, 2013 at 16:12 Comment(2)
+1, but I think in practice it doesn't matter much whether concurrent queues are FIFO or just random order. If you start 5 tasks in a loop, assume that they'll essentially start at the same time. There's no guarantee that e.g. the first I/O operation of the 1st task will occur before the 5th, even if they execute the same code. OTOH, for serial queues, the FIFO behavior is essential and IMHO this is the defining difference between the two queue types.Apartment
Incredile explanation. Claps a lot!Maag
S
38

Swift version

This is the Swift version of David's Objective-C answer. You use the global queue to run things in the background and the main queue to update the UI.

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
Sedimentation answered 8/7, 2016 at 14:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.