GCD: Obtaining Dispatch Queues

Obtaining Dispatch Queues

There are two ways to do that, with dispatch_queue_create and main dispatch queue / global dispatch queue. The following sections discuss both of these.

dispatch_queue_create

The dispatch_queue_create is a function for creating a dispatch queue. You can obtain a new dispatch queue with this function. Next, the source code shows how to create a serial dispatch queue. You can also create a concurrent dispatch queue as well. I explain it later in this section.

dispatch_queue_t mySerialDispatchQueue =
dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);

When you create a serial dispatch queue, it is independent of other serial queues, even though they all only execute one task at a time. For example, four separate serial queues with one task each, all started at the same time, would start executing simultaneously as shown in Figure 7–4.

images

Figure 7–4. Multiple Serial Dispatch Queues

We must know one thing related to that. When a serial dispatch queue is created and a task is added, the system creates one thread for each serial dispatch queue. If 2,000 serial dispatch queues are created, 2,000 threads are created. As I explained, too many threads consume too much memory and too many context switches cause the system to slow down as shown in Figure 7–5.

images

Figure 7–5. Problem of multiple Serial Dispatch Queues

That is why you should use a serial dispatch queue only to avoid inconsistent data (race condition) that occurs because multiple threads update the same data, which I explained as one of the problems of multithreaded programming (Figure7–6).

images

Figure 7–6. Situation where a Serial Dispatch Queue should be used

The number of serial dispatch queues should be the same amount that you need. For example, when you update a database, you should create one serial dispatch queue for each table. When you update a file, you should create a serial dispatch queue for the file or for each separated file block. Don’t create too many serial dispatch queues even if you think that you can create more threads than using a concurrent dispatch queue. If the tasks don’t cause problems such as inconsistent data and you want to execute them concurrently, you should use a concurrent dispatch queue. Even if a concurrent dispatch queue is created again and again, there is no problem because the queues just use threads that are managed efficiently by the XNU kernel.

Let’s get back to the dispatch_queue_create function. The first argument for the dispatch_queue_create function is the name of a serial dispatch queue. As in the source code, reverse-order FQDN is recommended for it. It is displayed as the name of the dispatch queue when you are debugging with Xcode or Instruments. The CrashLog that is generated when the application crashes includes that as well. So, it should be something understandable for you, as the application programmer, and not embarrassing for users. If you feel bothered, you can just use NULL. You’ll regret it when you debug the application, though.

For the second argument, you should pass NULL to create a serial dispatch queue. To create a concurrent dispatch queue, pass DISPATCH_QUEUE_CONCURRENT as follows.

dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create(
    "com.example.gcd.MyConcurrentDispatchQueue",  DISPATCH_QUEUE_CONCURRENT);

The return type of the dispatch_queue_create function is dispatch_queue_t, which is a variable type for dispatch queues. In all the previous examples, a variable “queue” is a dispatch_queue_t type.

dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create(
    "com.example.gcd.MyConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(myConcurrentDispatchQueue,
    ^{NSLog(@"block on myConcurrentDispatchQueue");});

In this source code, the Block runs in the concurrent dispatch queue.

Unfortunately, although the compiler has a great automatic memory management mechanism, ARC, application programmers have to release created dispatch queues manually because a dispatch queue, unlike a Block, isn’t treated as an Objective-C object. When you no longer need it, you have to call the dispatch_release function to release the dispatch queue that is created by dispatch_queue_create function.

dispatch_release(mySerialDispatchQueue);

As the name contains “release”, the “dispatch_retain” function is there as well.

dispatch_retain(myConcurrentDispatchQueue);

This means that dispatch queues have to be managed by the dispatch_retain and dispatch_release functions with the reference counting technique as for objects in Objective-C. In the previous source code, the concurrent dispatch queue that is created by the dispatch_queue_create function and is assigned to the variable “myConcurrentDispatchQueue” has to be released.

dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create(
    "com.example.gcd.MyConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(myConcurrentDispatchQueue,
    ^{NSLog(@"block on myConcurrentDispatchQueue");});

dispatch_release(myConcurrentDispatchQueue);

A concurrent dispatch queue executes tasks using multiple threads. In this example, the concurrent dispatch queue is released by the dispatch_release function just after a Block is added by the dispatch_async function. Does it work safely?

It works just fine. When a Block is added to a dispatch queue by a dispatch_async function, say, the Block has ownership of the dispatch queue by the dispatch_retain function. It is the same for both a serial and a concurrent dispatch queue. Then, when it finishes the execution of the Block, the Block releases the dispatch queue by the dispatch_release function.

Even if a dispatch queue is released just after a Block is added to the dispatch queue by the dispatch_async function, the dispatch queue still isn’t disposed of and the Block can be executed. After the Block is finished, the Block releases the dispatch queue and it is discarded. The dispatch_retain and dispatch_release functions are not only for dispatch queues. From now on, we will see many GCD APIs that contain “create” in their names. When you get something from these APIs, you have to release them with the dispatch_release function when you don’t need them anymore. When you get them via some other method, you have to take ownership by the dispatch_retain function and you have to release them by the dispatch_release function.

Main Dispatch Queue/Global Dispatch Queue

The other way to obtain a dispatch queue is to get a dispatch queue that the system has already provided. Actually, the system has dispatch queues that you don’t create: the main dispatch and global dispatch queues.

The main dispatch queue is, as “main” is in its name, a queue to execute tasks on the main thread. As there exists only one main thread, the main dispatch queue is a serial dispatch queueTasks in the main dispatch queue are executed in RunLoop on the main thread as shown in Figure 7–7. Because they are executed on the main thread, you should use it for tasks that must be done on the main thread such as updating the user interface, and the like. It is just like the performSelectorOnMainThread instance method of the NSObject class.

images

Figure 7–7. Main Dispatch Queue

The other queues that the system provides are called global dispatch queues. They are concurrent dispatch queues that are usable anywhere in the application. If you don’t have any specific reason, as explained later, you don’t need to create a concurrent dispatch queue by the dispatch_queue_create function in most cases. You can just obtain a global dispatch queue and use it.There are four global dispatch queues and they each have a different priority: high, default, low, and background. The XNU kernel manages threads for the global dispatch queues and the threads have priorities corresponding to each priority of the global dispatch queues. When you add tasks to a global dispatch queue, you should choose a global dispatch queue having the appropriate priority. The XNU kernel doesn’t assure the realtimeness for threads. So, the priorities can be used just as a guide. For example, you should use a background priority when you don’t much care if a task is executed. Let’s see what types of dispatch queues are provided in the system in Table 7–2.

images

Each dispatch queue can be obtained as shown in Listing 7–1.

Listing 7–1. Obtaining dispatch queues

 /*
  * How to get the main dispatch queue
  */
dispatch_queue_t mainDispatchQueue =dispatch_get_main_queue();

 /*
  * How to get a global dispatch queue of high priority
  */
dispatch_queue_t globalDispatchQueueHigh =
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

 /*
  * How to get a global dispatch queue of default priority
  */
dispatch_queue_t globalDispatchQueueDefault =
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

 /*
  * How to get a global dispatch queue of low priority
  */
dispatch_queue_t globalDispatchQueueLow =
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

 /*
  * How to get a global dispatch queue of background priority
  */
dispatch_queue_t globalDispatchQueueBackground =
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

By the way, if you call the dispatch_retain or dispatch_release function on the main dispatch queue or on a global dispatch queue, nothing happens. No problem. That is why it is easier to obtain and use a global dispatch queue than to create, use, and release a concurrent dispatch queue yourself. Of course, depending on your source code, if it is easier for the dispatch queue to be treated as it is created by the dispatch_queue_create function, you can follow the concept of reference counting rules and call the dispatch_retain or dispatch_release function even for the main dispatch queue or a global dispatch queue.

At the end of this section, there is an example of how to use the main dispatch queue and global dispatch queues as shown in Listing 7–2.

Listing 7–2. Executing tasks

 /*
  * Execute a Block on a global dispatch queue of default priority.
  */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

     /*
      * some tasks here to be executed concurrently
      */

     /*
      * Then, execute a Block on the main dispatch queue
      */

    dispatch_async(dispatch_get_main_queue(), ^{

         /*
          * Here, some tasks that can work only on the main thread.
          */
    });

});

We’ve learned the basics of GCD for making tasks run in parallel. In the following section, I explain APIs to control the queues to make them more useful.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值