Multi-threaded development is an indispensable part of daily development tasks. Multi-threaded development technologies commonly used in iOS development include GCD, NSOperation and NSThread. This paper mainly explains the knowledge and detailed usage of NSOperation in multi-line series articles.

  1. IOS multithreading: GCD details
  2. IOS multithreading: NSOperation details

1. Introduction to GCD

GCD is no stranger to iOS developers. In actual development, we will often use GCD for multithreading. So what is GCD?

Grand Central Dispatch (GCD) is a relatively new solution to multi-core programming developed by Apple. It is primarily used to optimize applications to support multicore processors and other symmetric multiprocessing systems. It is a concurrent task executed on a thread pool basis. First released on Mac OS X 10.6 Snow Leopard and also available on iOS 4 and above.

GCD has obvious advantages that make it very important to deal with multithreading problems.

  1. GCD is apple’s solution for multi-core parallel computing.
  2. GCD can make better use of CPU core resources.
  3. GCD does not require the developer to manage the thread lifecycle.
  4. Easy to use, the developer only needs to tell the GCD what tasks to perform and does not need to write any thread management code.

2. GCD tasks and queues

The relationship between GCD tasks and queues is confusing to many beginning developers, but queues simply provide containers for holding tasks. To better understand GCD, it is important to understand the concepts of tasks and queues.

2.1 the GCD task

A task is an operation that needs to be performed, and is a piece of code in GCD that is placed in a block and executed in a thread. There are two execution modes for a task: synchronous execution and asynchronous execution. The main differences are the ability to wait for queued tasks to finish and the ability to start new threads.

  • Sync: Synchronously adds a task to a queue and waits until the execution of the previous task in the queue ends. Synchronous tasks can only be executed in the current thread and do not have the ability to start a new thread.
  • Async: Asynchronously adds a task to a queue and takes care of other tasks in the queue. Asynchronous execution can be performed in a new thread, with the ability to start a new thread.

2.2 GCD queue

Queue: A queue is a special linear table. The end of the queue that allows insert operations is called the end of the queue, and the end of the queue that allows delete operations is called the head. It is a first-in, first-out structure. In GCD, queue refers to the waiting queue for executing tasks. It is used to store tasks. By the structural nature of the queue, new tasks are always inserted at the end of the queue, and the execution of the task is always output from the head of the queue, and each task read is released from the queue. GCD queues are divided into serial queues and concurrent queues, both of which comply with FIFO (first-in, first-out) principles. The main differences between the two are: the execution order is different, and the number of threads started is different.

  • Serial queue: Only one thread can be started. Only one task can be executed at a time. The next task can be executed after the execution is complete.
  • Concurrent queue: It is possible to execute tasks simultaneously, that is, to open multiple threads, so that multiple tasks can be executed at the same time.

The differences between the two are as follows:

3. Basic use of GCD

GCD is simple to use. First create a queue and then append tasks to the queue. The system will execute tasks according to the type of tasks.

3.1 Creation of queues

  1. Creating a queue is simple, just a calldispatch_queue_createMethod passes in the corresponding parameters. This method takes two arguments:
  • The first parameter represents the unique identity of the queue and can be passed to null.
  • The second parameter identifies whether the queue is serial or concurrent.DISPATCH_QUEUE_SERIALRepresents a serial queue,DISPATCH_QUEUE_CONCURRENTRepresents a concurrent queue.
Dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_SERIAL); Dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
Copy the code
  1. GCD provides one by defaultGlobal concurrent queue, the calldispatch_get_global_queueMethod to get the global concurrent queue. This method takes two arguments.
  • The first argument is a long integer that represents the queue priorityDISPATCH_QUEUE_PRIORITY_HIGH,DISPATCH_QUEUE_PRIORITY_LOW,DISPATCH_QUEUE_PRIORITY_BACKGROUND,DISPATCH_QUEUE_PRIORITY_DEFAULTFour choices, generally usedDISPATCH_QUEUE_PRIORITY_DEFAULT.
  • The second argument is useless for the moment, just use 0.
Dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);Copy the code
  1. GCD is provided by defaultThe home side column, the calldispatch_get_main_queueMethod, all tasks placed in the main queue are executed in the main thread. A primary queue is a serial queue.
// dispatch_queue_t mainQueue = dispatch_get_main_queue();Copy the code

3.2. Create a task

GCD calls dispatch_sync to create synchronization tasks and dispatch_async to create asynchronous tasks. The contents of the task are all in the block.

// Dispatch_async (queue, ^{// async code}); // sync dispatch_sync(queue, ^{// sync execution code});Copy the code

3.3. Combination of tasks and queues

Created to perform tasks need to be put in the queue, at the same time, considering the particularity of the home side columns, as in the case of no account of nested task will exist synchronization task + serial asynchronous task queue, synchronization task + concurrent queue, serial asynchronous task queue, + + concurrent queue, the home side column + synchronous task, the home side column + asynchronous task of six kinds of combinations, Let’s take a look at these combinations.

  • Synchronization task + Serial queue: The synchronization task does not start new threads, and the task is executed in serial.
  • Synchronization task + Concurrent queue: A synchronization task does not start new threads. Although the task is in a concurrent queue, the system starts only one main thread by default, but does not start child threads. Therefore, the task is executed in sequence.
  • Asynchronous task + serial queue: New threads are started for asynchronous tasks and the tasks are executed in serial.
  • Asynchronous task + Concurrent queue: A new thread is started for an asynchronous task and the task is executed concurrently.
  • Primary queue + Synchronization task: A primary queue is a serial queue. Tasks are executed in the main queue in serial mode. Adding a synchronization task to the primary queue blocks the main queue, resulting in a deadlock.
  • Main queue + asynchronous task: The main queue is a serial queue. The tasks are executed in serial in the main thread. Even if the asynchronous tasks are appended, no new thread will be started.

Let’s look at the use of various combinations.

3.4 Basic use of GCD

3.4.1 Synchronization task + Serial Queue

- (void)syncTaskWithSerial {
    NSLog(@"currentThread:%@", [NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"currentThread-1:%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"currentThread-2:%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"currentThread-3:%@", [NSThread currentThread]);
    });
}
Copy the code

CurrentThread [51144:6948582] currentThread:

{number = 1, Name = main} ThreadDemo[51144:6948582] ThreadDemo[51144:6948582] 0x600001739100>{number = 1, Name = main} ThreadDemo[51144:6948582] ThreadDemo[51144:6948582] 0x600001739100>{number = 1, Name = main} ThreadDemo[51144:6948582] ThreadDemo[51144:6948582]

{number = 1, name = main}

As you can see from the results of the above code run, no new threads are started and the tasks are executed sequentially.

3.4.2 Synchronization Task + Concurrent Queue

- (void)syncTaskWithConcurrent {
    NSLog(@"current thread:%@", [NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"current thread-1:%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"current thread-2:%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"current thread-3:%@", [NSThread currentThread]);
    });
}
Copy the code

2020-03-12 21:39:45.931001+0800 ThreadDemo[51225:6953218] current thread:

{number = 1, Name = main} 2020-03-12 21:39:45.931259+0800 ThreadDemo[51225:6953218] current thread-1:

{number = 1, Name = main} 2020-03-12 21:39:45.931442+0800 ThreadDemo[51225:6953218] current thread-2:

{number = 1, Name = main} 2020-03-12 21:39:45.931606+0800 ThreadDemo[51225:6953218] current thread-3:

{number = 1, name = main}



As can be seen from the results of the above code running, the synchronization task will not start a new thread, although the task is in the concurrent queue, but the system only starts a main thread by default, there is no child thread, so the task is executed in serial.

3.4.3 Asynchronous task + Serial queue

- (void)asyncTaskWithSeria{
    NSLog(@"current thread:%@", [NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"current thread-1:%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"current thread-2:%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"current thread-3:%@", [NSThread currentThread]);
    });
    NSLog(@"4");
}
Copy the code

2020-03-12 21:44:22.369058+0800 ThreadDemo[51283:6957598] current thread:

{number = 1, Name = main} 2020-03-12 21:44:22.369279+0800 ThreadDemo[51283:6957598] 4 2020-03-12 21:44:22.369346+0800 ThreadDemo[51283:6958684] current thread-1:

{number = 7, Name = (null)} 2020-03-12 21:44:22.369511+0800 ThreadDemo[51283:6958684] current thread-2:

{number = 7, Name = (null)} 2020-03-12 21:44:22.369675+0800 ThreadDemo[51283:6958684] current thread-3:

{number = 7, name = (null)}



As can be seen from the results of the above code run, starting a new thread indicates that the asynchronous task has the ability to start a new thread, but since the task is executed in a serial queue, the task is executed sequentially.

3.4.4 Asynchronous task + Concurrent queue

- (void)asyncTaskWithConcurrent{
    NSLog(@"current thread:%@", [NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"current thread-1:%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"current thread-2:%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"current thread-3:%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"current thread-4:%@", [NSThread currentThread]);
    });
}
Copy the code

2020-03-12 21:59:36.511770+0800 ThreadDemo[51635-6976397] current thread:

{number = 1, Name = main} 2020-03-12 21:59:36.512015+0800 ThreadDemo[51635-6976575] current thread 2:

{number = 5, Name = (null)} 2020-03-12 21:59:36.512011+0800 ThreadDemo[51635-6976577] current thread1 :

{number = 4, Name = (null)} 2020-03-12 21:59:36.512028+0800 ThreadDemo[51635-6976580] current thread-3:

{number = 6, Name = (null)} 2020-03-12 21:59:36.512035+0800 ThreadDemo[51635-6976578] current thread-4:

{number = 7, name = (null)}




As you can see from the results of the above code, multiple threads were generated and the task was executed randomly (concurrently).

3.4.5 Main queue + Synchronization task

-(void)syncTaskWithMain{
    NSLog(@"currentThread---%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"1 - % @",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"2 - % @",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"3 - % @",[NSThread currentThread]);
    });
    
    NSLog(@"4");
}
Copy the code

ThreadDemo[51754:6982402] currentThread–

{number = 1, name = main} (lldb)

It’s obvious that the above code crashed because we put the syncTaskWithMain task in the main thread. However, synchronous execution will wait until the tasks in the current queue are completed. So when we append task 1 to the main queue, task 1 is waiting for the main thread to finish processing the syncTaskWithMain task. The syncMain task needs to wait for task 1 to complete, resulting in a mutual waiting situation, resulting in a deadlock.

3.4.6 Main queue + Asynchronous task

-(void)asyncTaskWithMain{
    NSLog(@"currentThread---%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"1 - % @",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2 - % @",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"3 - % @",[NSThread currentThread]);
    });
    
    NSLog(@"4");
}
Copy the code

2020-03-12 22:09:49.285203+0800 ThreadDemo[51832:6986908]

{number = 1, Name = main} 2020-03-12 22:09:49.285539+0800 ThreadDemo[51832:6986908] 4 2020-03-12 22:09:49.326310+0800 ThreadDemo[51832:6986908] 1—

{number = 1, name = main} 2020-03-12 22:09:49.326749+0800 ThreadDemo[51832:6986908] 2–

{number = 1, name = main} 2020-03-12 22:09:49.326988+0800 ThreadDemo[51832:6986908] 3–

{number = 1, name = main}\



As you can see from the results of the above code, although the task is asynchronous, no new thread is started, it is still executed in the main thread, and the task is executed sequentially.

3.5. Use of nested tasks

There are many different scenarios for nested tasks, so here’s a quick summary,

The difference between “Asynchronous execution + Concurrent queue” nested “Same concurrent queue” “Synchronous execution + Concurrent queue” nested “Same concurrent queue” “Asynchronous execution + Serial queue” nested “same serial queue” “Synchronous execution + Serial Queue” nested “Same Serial queue”
synchronous No new thread is started and the task is executed sequentially No new thread is started and the task is executed sequentially The deadlock is stuck and will not be executed The deadlock is stuck and will not be executed
asynchronous Start a new thread and execute tasks concurrently Start a new thread and execute tasks concurrently Start a new thread (1 thread) to execute tasks in serial Start a new thread (1 thread) to execute tasks in serial

For “asynchronous execution + serial queue” nested “same serial queue” deadlock situation please see the following code:

dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_SERIAL); // Asynchronous task A dispatch_async(queue, ^{NSLog(@)"The task - C % @",[NSThread currentThread]);
    });
});
Copy the code

First asynchronous tasks into the queue, A sync task is code execution for asynchronous task A part B, C for synchronization task B code execution part, because it is in serial queue, task is serial execution, according to the queue “first-in first-out” principle, the first thing you need to remove the task A, namely the part B, but A B C, C waits for B to complete execution, thus forming a mutual wait, resulting in deadlock deadlock.

The deadlock caused by “synchronous execution + serial queue” nested with “same serial queue” is analyzed in the same way.

4. Communication between GCD threads

In the iOS development process, we refresh the UI in the main thread, and put some time-consuming operations such as picture download, file upload and network request into other threads. When these time-consuming operations are completed and the data needs to be synchronized to the UI, we need to return to the main thread to refresh the UI, which requires communication between threads. The GCD provides a very easy way to communicate between threads.

- (void)communication {
    dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"1 - % @",[NSThread currentThread]); Sleep (2); // dispatch_async(dispatch_get_main_queue(), ^{NSLog(@)"2 - % @",[NSThread currentThread]); // Prints the current thread}); }); }Copy the code

2020-03-13 00:06:07.632014+0800 ThreadDemo[5348:7074047] 1–

{Number = 5, name = (null)} 2020-03-13 00:06:09.633041+0800 ThreadDemo[53480:7073841] 2–

{number = 1, name = main}\

As you can see from the results of the above code run, 1 is executed in the child thread, and 2 is printed after 2 seconds, and 2 is executed in the main thread.

5. Function methods in GCD

GCD provides a variety of function methods for developers to call, let’s take a look at the use of these methods.

5.1. Fence method

There are times when we need to perform two sets of operations asynchronously and wait for the first set to complete before going back to the second set. This is when the fence method comes into play. The fence method (dispatch_barrier_async or dispatch_barrier_sync) will wait for the task appended to the queue to complete before it is appended to the queue. Then wait until the dispatch_barrier_async or dispatch_barrier_sync method to execute the task appended to the queue. Simply put, dispatch_barrier_ASYNc or dispatch_barrier_sync splits asynchronous tasks into two groups. After executing the first group, execute yourself and then the rest of the tasks in the queue. The only difference is that dispatch_barrier_Async does not block the thread.

Look at the following code:

- (void)barrierTask {
    dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"start");
    dispatch_async(queue, ^{
        NSLog(@"currentThread-1:%@", [NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"currentThread-2:%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"pause");
    dispatch_async(queue, ^{
        NSLog(@"currentThread-3:%@", [NSThread currentThread]);
    });
    NSLog(@"end");
}
Copy the code

Print result: 2020-03-13 00:31:53.247514+0800 ThreadDemo[54101:7100220] start 2020-03-13 00:31:53.247730+0800 ThreadDemo[54101:7100396] ThreadDemo[54101:7100396] CurrentThread-1 :

{number = 5, Name = (NULL)} 2020-03-13 00:31:53.247883+0800 ThreadDemo[54101:7100220] end 2020-03-13 00:31:53.247991+0800 ThreadDemo[54101:7100396] currentThread-2:

{number = 5, Name = (null)} 2020-03-13 00:31:55.250622+0800 ThreadDemo[54101:7100396] 0x600003b8db00>{number = 5, name = (null)}

Dispatch_barrier_async (2) dispatch_barrier_async (2)

What happens if dispatch_barrier_async is replaced with dispatch_barrier_sync?

- (void)barrierTask {
    dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"start");
    dispatch_async(queue, ^{
        NSLog(@"currentThread-1:%@", [NSThread currentThread]);
    });
    dispatch_barrier_sync(queue, ^{
        NSLog(@"currentThread-2:%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"pause");
    dispatch_async(queue, ^{
        NSLog(@"currentThread-3:%@", [NSThread currentThread]);
    });
    NSLog(@"end");
}
Copy the code

Print result: 2020-03-13 00:35:01.460109+0800 ThreadDemo[54171:7103212] start 2020-03-13 00:35:01.460408+0800 ThreadDemo[54171:7103379] currentThread-1:

{number = 6, Name = (null)} 2020-03-13 00:35:01.460588+0800 ThreadDemo[54171:7103212] currentThread-2:

{number = 1, Name = main} 2020-03-13 00:35:03.461678+0800 ThreadDemo[54171:7103212] Pause 2020-03-13 00:35:03.462012+0800 ThreadDemo[54171:7103212] ThreadDemo[54171:7103379] ThreadDemo[54171:7103379] ThreadDemo[54171:7103212] end 0x600002508540>{number = 6, name = (null)}

If “pause” and “end” are executed after “2” then “dispatch_barrier_sync” is executed after “2”.

5.2. Delay execution method -dispatch_after

It’s no surprise to iOS developers that we often encounter the need to perform an action after a specified delay, so it’s very convenient to implement this requirement with GCD. GCD’s delayed execution function is dispatch_after. Note that the dispatch_after method does not start processing after a specified time, but appends tasks to the main queue after a specified time. This time is not strictly accurate, but the dispatch_after method is useful for roughly delaying the execution of tasks.

-(void)afterTask{
    NSLog(@"begin");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"after---%@",[NSThread currentThread]);
    });
}
Copy the code

Print result: 2020-03-13 00:43:50.787769 +0800 ThreadDemo[54378:7111012] BEGIN 2020-03-13 00:43:50.788086+0800 ThreadDemo[54378:7111012] after—

{number = 1, name = main}

From the results of the above code, you can see that AFER is printed 3 seconds after BEGIN.

5.3. One-time code -dispatch_once

GCD provides a once-only method called dispatch_once, which is often used when creating singletons. The dispatch_once method ensures that a piece of code is called only once during program execution and is thread-safe in multithreaded environments.

+ (instancetype)shareInstance{
    static Test *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [Test alloc]init];
    });
    return instance;
}
Copy the code

5.5 scheduling group

In a scheduling group, asynchronously executed tasks are grouped and executed in a specified thread after all tasks are executed. The call group uses dispatch_group_create to create a group. The dispatch_group_async method adds tasks to the queue and queue parties to the scheduling group. Alternatively, you can add a queue to a dispatch group using dispatch_group_Enter and dispatch_group_leave pairings. Call dispatch_group_notify to return to the specified thread, or call dispatch_group_wait to block the current thread.

- (void)groupNotifyTest{
    NSLog(@"current thread:%@", [NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"thread-1:%@", [NSThread currentThread]);
    });

    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"thread-2:%@", [NSThread currentThread]);
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"thread-3:%@", [NSThread currentThread]);
        NSLog(@"group-end"); }); // dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"Continue after dispatch_group_wait");
}
Copy the code

2020-03-14 23:58:09.871023+0800 ThreadDemo[77370:8259501] current thread:

{number = 1, name = main} 2020-03-14 23:58:11.874345+0800 ThreadDemo[77370:8260290] thread2 :

{number = 7, Name = (null)} 2020-03-14 23:58:11.874343+0800 ThreadDemo[77370:8259684] thread1 :

{number = 5, Name = (NULL)} 2020-03-14 23:58:11.874672+0800 ThreadDemo[77370:8259501] Dispatch_group_wait 23:58:13. 877077 + 0800 ThreadDemo [77370-8259501] thread – 3: < NSThread: 0x6000019FE480 >{number = 1, name = main} 2020-03-14 23:58:13.877365+0800 ThreadDemo[77370:8259501] group-end


One thing to note here is dispatch_group_wait, which takes two parameters, the first parameter is group (dispatch group) and the second parameter is timerout (wait time). Once dispatch_group_WAIT is called, the dispatch_group_wait function processes the status of the call without returning a value, except when currentThread stops, the wait time specified by the wait function has been reached, or until all operations in the Dispatch Group have completed. The thread executing the function stops. A timeout of DISPATCH_TIME_FOREVER means a permanent wait; When timeout is specified as DISPATCH_TIME_NOW, it means that all the dispatch_group processes can be determined without waiting. If the dispatch_group_wait function does not return a value of 0, it means that the operations in the Dispatch Group have not been completed despite the time specified. If the dispatch_group_wait function returns a value of 0, it means that all operations in the Dispatch Group have been performed.

The dispatch_group_Enter and dispatch_group_leave pairings implement adding a queue to a dispatch group. “Dispatch_group_enter” indicates that the number of tasks in the group is +1. “group_leave” indicates that the number of tasks in the group is -1, indicating that a task is completed.

- (void)groupEnterTest {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_enter(group);

    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"thread_1:%@", [NSThread currentThread]);
        dispatch_group_leave(group);
    });

    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"thread_2:%@", [NSThread currentThread]);
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"thread_3:%@", [NSThread currentThread]);
        NSLog(@"group_end");
    });
}
Copy the code

ThreadDemo[77525:8269621] thread_2:

{number = 7, Name = (null)} 2020-03-15 00:07:17.293320+0800 ThreadDemo[77525:8269427] thread_1:

{number = 3, Name = (null)} 2020-03-15 00:07:19.294186+0800 ThreadDemo[77525:8269235] thread3 :

{number = 1, name = main} 2020-03-15 00:07:19.294485+0800 ThreadDemo[77525:8269235] Group_end


Note that ‘dispatch_group_enter’ and ‘dispatch_group_leave’ pairs occur.

5.6. Semaphore

A GCD Semaphore is a Dispatch Semaphore that holds counts. When the semaphore is less than zero it will wait until the thread is blocked, otherwise it will execute normally. Semaphores can keep threads synchronized, converting asynchronously executed tasks to synchronous tasks while keeping threads safe.

Dispatch Semaphore offers three methods:

  • dispatch_semaphore_create: Creates a Semaphore and initializes the total amount of signals
  • dispatch_semaphore_signal: Sends a signal that increases the total number of signals by 1
  • dispatch_semaphore_wait: You can decrease the total semaphore by 1. If the total number of signals is less than 0, the thread will wait (blocking thread), otherwise it can execute normally.
- (void)semaphoreTest {
   dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block int a = 0;
    while (a < 5) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"The value of a inside: %d-----%@", a, [NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
            a++;
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
    NSLog(@"The value of a outside: %d", a);
}
Copy the code

2020-03-15 00:44:53.005899+0800 ThreadDemo[78303:8318841] = 0—–

{number = 3, name = (null)} 2020-03-15 00:44:53.006161+0800 ThreadDemo[78303:8318841] 1—–

{number = 3, name = (null)} 2020-03-15 00:44:53.006354+0800 ThreadDemo[78303:8318841] 2—–

{number = 3, name = (null)} 2020-03-15 00:44:53.006551+0800 ThreadDemo[78303:8318841] 3—–

{number = 3, name = (null)} 2020-03-15 00:44:53.006727+0800 ThreadDemo[78303:8318841] 4—–

{Number = 3, Name = (NULL)} 2020-03-15 00:44:53.006862+0800 ThreadDemo[78303:8318672




5.7. Dispatch source -dispatch_source

Scheduling sources are the basic data types that coordinate the processing of special low-level system events. GCD supports timer scheduling sources, signal scheduling sources, descriptor scheduling sources, process scheduling sources, port scheduling sources, and custom scheduling sources. Dispatch Sources have a series of mature apis, which are not described here and can be referred to the official document Dispatch Sources for details.