Grand Central Dispatch (GCD) is an API based on C language. It is a solution proposed by Apple for multi-core parallel computing. The GCD automatically takes advantage of more CPU cores (such as dual-core, quad-core). The programmer simply adds the task to the queue and specifies the function that performs the task. There is no need to write any thread management code.
Before learning about GCD, let’s look at the two core concepts in GCD: tasks and queues.
One. Task-task
Task: A piece of code that you execute in a thread. In GCD it is placed in a block.
There are two ways to execute a task: synchronous execution and asynchronous execution. The main differences between the two are whether to wait for queued tasks to finish and whether to have the ability to start a new thread.
- Synchronization task
dispatch_sync
- Synchronously adds tasks to the specified queue
Waiting for the
The next task can be executed only after all tasks in the current queue are completed - You can only execute tasks in the current thread and do not have the ability to start new threads.
- Asynchronous tasks
dispatch_async
- Asynchronously adds tasks to the specified queue, which does nothing
Waiting for the
, you can continue to perform the task. - You can execute tasks in new threads and have the ability to start new threads
- Note: Asynchronous execution, while having the ability to start a new thread, does not necessarily start a new thread. This depends on the type of queue specified by the task (described below)
2. Queue – Dispatch Queue
Dispatch Queue: The Queue refers to the waiting Queue for executing tasks, that is, the Queue for storing tasks. A queue is a special linear table, which adopts the first-in-first-out (FIFO) principle, that is, new tasks are always inserted at the end of the queue, and tasks are always read from the head of the queue. Each time a task is read, a task is released from the queue. The structure of the queue can be seen in the following figure:
There are two types of queues in GCD: serial queues and concurrent queues. Both comply with the FIFO principle. The main differences between the two are: the execution order is different, and the number of threads started is different.
- Serial queues
Serial Dispatch Queue
One at a timetask
Be executed, apply for only onethread
, atask
After the command is executed, perform the next command
Concurrent Dispatch Queue
thread
task
Iii. Use of THE COMMUNIST Party CD
GCD is easy to use, there are only three steps
- create
task
(Synchronous task or asynchronous task) - create
The queue
(Serial queue or concurrent queue) - will
task
Wait to append to taskThe queue
, the system then executes the task (synchronous or asynchronous) according to the task type.
3.1 Task Creation
“Dispatch_sync” is used for synchronous execution and “dispatch_async” is used for asynchronous execution.
Dispatch_sync (queue, ^{}); Dispatch_async (queue, ^{dispatch_async});Copy the code
3.2 Creating queues
GCD can create queues using dispatch_queue_creat
dispatch_queue_t queue - dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>);
Copy the code
const char * _Nullable label
: Unique identifier of the queue, used for debug, can be null.dispatch_queue_attr_t _Nullable attr
: Queue type.DISPATCH_QUEUE_SERIAL
Serial queue,DISPATCH_QUEUE_CONCURRENT
Concurrent queues.
The second argument, if passed NULL, creates a serial queue.
The home side column
For serial queues, GCD provides: Main Queue – Main Dispatch Queue by default
- All tasks placed in the main queue are executed in the main thread
- You can use
dispatch_get_main_queue()
Method to obtain the main queue
The main queue is essentially a normal serial queue, but by default, the code is placed in the main queue, and the code in the main queue is executed in the main program, giving us a special feel for the main queue.
Global concurrent queue
For concurrent queues, GCD provides: Global Dispatch Queue by default. Global concurrent queues can be obtained using the dispatch_get_global_queue() method
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
dispatch_queue_t queue = dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>)
Copy the code
long identifier
: indicates the queue priority. Common useDISPATCH_QUEUE_PRIORITY_DEFAULT
- It doesn’t work yet. You can pass a zero
4. Different combinations of tasks and queues
4.1 Synchronization Task + Serial Queue
- (void)syncSerial
{
NSLog(@"Main thread - %@", [NSThread currentThread]);
NSLog(@"---begin---");
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"Synchronization task + serial queue: %ld -- %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"---end---");
}
Copy the code
- All tasks are executed in the current thread (main thread) and no new thread is opened. (
A synchronization task can only perform tasks in the current thread and does not have the ability to start a new thread
) - All prints are executed between –begin and –end (
Synchronization tasks wait for the completion of tasks in the queue
) - Tasks are executed sequentially (
A serial queue has only one task executed at a time, and the tasks are executed sequentially, one after the other
).
4.2 Asynchronous Task + Serial Queue
- (void)asyncSerial
{
NSLog(@"Main thread - %@", [NSThread currentThread]);
NSLog(@"---begin---");
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"Asynchronous task + serial queue: %ld -- %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"---end---");
}
Copy the code
- A new thread is started (
Asynchronous tasks have the ability to start threads, whereas serial queues only start one thread
) - All missions are executed after begin and end (
Asynchronous execution does not do any waiting and can proceed to the next task
) - Tasks are executed sequentially (
A serial queue has only one task executed at a time, and the tasks are executed sequentially, one after the other
)
4.3 Synchronization Task + Parallel Queue
- (void)syncConcurrent
{
NSLog(@"Main thread - %@", [NSThread currentThread]);
NSLog(@"---begin---");
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"Synchronous task + parallel queue: %ld -- %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"---end---");
}
Copy the code
- All tasks are executed in the current thread (the main thread), without opening a fine thread (
A synchronization task can only perform tasks in the current thread and does not have the ability to start a new thread
) - All prints are executed between –begin and –end (
Synchronization tasks wait for the completion of tasks in the queue
) - Tasks are performed in sequence: Reason: Although
Concurrent queue
You can start multiple threads and perform multiple tasks at the same time. But since new threads cannot be created, there is only one thread, the current thread (The synchronization task does not have the ability to start new threads
), so there is no concurrency. And the current thread must wait for the tasks currently in the queue to complete before it can continue to perform the following operations (Synchronization tasks wait for the completion of tasks in the queue
). So tasks can only be executed sequentially, not simultaneously.
4.4 Asynchronous Task + Parallel Queue
- (void)asyncConcurrent
{
NSLog(@"Main thread - %@", [NSThread currentThread]);
NSLog(@"---begin---");
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"Asynchronous task + parallel queue: %ld -- %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"---end---");
}
Copy the code
- In addition to the current thread (main thread), multiple threads are started and tasks are executed alternately/simultaneously. (
Asynchronous tasks have the ability to start new threads. Concurrent queues enable multiple threads to perform multiple tasks simultaneously
). - All missions are executed after begin and end (
Asynchronous execution does not do any waiting and can proceed to the next task
)
4.5 Synchronization Task + Main Queue
The synchronization task + main queue is called differently in different threads, and deadlocks occur when called in the main thread, but not in other threads.
- Main thread call
Synchronization task + main queue
The main queue is a serial queue. All tasks in the main queue are executed in the main thread. When we execute the syncMain method in the main thread, we put the syncMain 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 syncMain task. The syncMain task waits for task 1 to complete before continuing. The two tasks wait for each other to complete.
- Child thread call
Synchronization task + main queue
// Using the NSThread detachNewThreadSelector method creates the thread, [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];Copy the code
- All tasks are executed in the main thread (not the current thread), no new thread is opened (
All tasks placed in the main queue are executed in the main thread
All prints are executed between –begin and –end (Synchronization tasks wait for the completion of tasks in the queue
) - Tasks are performed sequentially (
The main queue is a serial queue where only one task is executed at a time, one after the other in sequence
)
Why isn’t it stuck now?
Because the syncMain task goes into the child thread; Tasks 1 through 10 are appended to the main queue and executed in the main thread. No task is being executed in the main queue. Therefore, task 1 in the main queue is executed directly. After task 1 is completed, task 2 is executed. So there is no thread blocking and therefore no deadlock problem.
4.6 Nested Use
The following demos are all in the main queue by default, because the main queue is a serial queue and the code is executed from top to bottom. By default, asynchronous operations are time-consuming.
- Nested synchronization in serial asynchrony
- (void)demo1
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(queue, ^{
sleep(2);
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5"); } Print order: 1, 5, 2 deadlockCopy the code
The code block B
Wait and block for synchronization task. So,Task 4
Wait until theThe code block B
Only after the command is executed can it be executed.The code block B
To finish, we have to waitTask 3
The execution is complete.- Because it’s a serial queue, it follows the FIFO.
Task 3
Wait until theTask 4
Execute the command only after the command is executed - So,
Task 4
等Block B
.Block B
等Task 3
.Task 3
等Task 4
. A deadlock.
- Nested asynchrony in serial synchronization
- (void)demo2
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_sync(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
sleep(2);
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5"); } Print order: 12453Copy the code
- Main queue, code executed in sequence: join queue in sequence
Task 1 code block A Task 5
.Block A,
Yes synchronization task. Wait. So print 1 code block A 5 Block A,
In: join the queue in sequenceTask 2 block B Task 4
.The code block B
It is an asynchronous task that does not wait and takes time. So print 2, 4, 3The code block B
Print 5 after the command is executed- Synchronization ensures that 4 must be printed before 5
- Nested synchronization in parallel asynchrony
- (void)demo3
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
sleep(2);
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5"); } Print result: 15234Copy the code
- Main queue, code executed in sequence: join queue in sequence
Task 1 code block A Task 5
.Block A,
It is an asynchronous task that does not wait and takes time. So print 1, 5 Block A,
Inside: parallel queue, apply for new thread. New threads are added in turnTask 2 block B Task 4
.The code block B
Synchronization task, waiting to block the current child thread. Print 2, 3, 4- Synchronization ensures that 3 must be printed before 4
- Nested asynchrony in parallel synchronization
- (void)demo4
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_sync(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
sleep(2);
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5"); } Print result: 12453Copy the code
- Main queue, code executed in sequence: join queue in sequence
Task 1 code block A Task 5
.Block A,
Yes synchronization task. Wait. So print 1 code block A 5 Block A,
In: Parallel queue, joinTask 2 block B Task 4
.The code block B
Asynchronous task, no waiting, time consuming, applying for new thread.- Synchronization ensures that 4 is printed before 5
5. Application of GCD
5.1 Method of dispatch_after Delayed execution
Application scenario: Execute a task after the specified time.
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.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"Output in 2 seconds.");
});
Copy the code
5.2 Dispatch_once One-time code
Application scenario: Singleton, method-Swizzling
Dispatch_once ensures that a piece of code is executed only once during the execution of a program, and even in a multithreaded environment, dispatch_once ensures thread-safety.
- (void)once { static dispatch_once_t onceToken; Dispatch_once (&onceToken, ^{// only execute code once(thread-safe by default)}); }Copy the code
5.3 Dispatch_Apply Rapid Iteration
Application scenario: Calculate the size of each control in advance after obtaining network data
Dispatch_apply adds tasks to a specified queue based on the specified number of times and waits for all queues to complete.
- The serial queue uses dispatch_apply, which is executed sequentially and synchronously, just like the for loop. But this misses the point of rapid iteration.
- We can use concurrent queues for asynchronous execution. For example, to iterate over the numbers 0 to 5, the for loop takes one element at a time and iterates through it. Dispatch_apply can traverse multiple numbers simultaneously (asynchronously) in multiple threads.
- (void)apply
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
NSLog(@"Before the dispatch_apply");
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"Dispatch_apply -- thread %zu-%@", index, [NSThread currentThread]);
});
NSLog(@"After dispatch_apply");
}
Copy the code
Synchronous operation
dispatch_group_wait
5.4 Dispatch_barrier function
Application scenario: Thread synchronization
Asynchronous task + concurrent queue will open up threads, and multiple tasks will be executed at the same time. Each task will be out of order due to task complexity and CPU scheduling. The fence function is a good way to set up the sequence of tasks to complete.
-
Dispatch_barrier_async Is used to submit asynchronous execution of a fence block task and return immediately. This function does not block the thread, only the task execution. The fence function does not execute immediately after submission, but returns directly. After all the tasks submitted before the fence function are completed, the fence function task will be automatically executed, and the tasks submitted after the fence function must be executed after the fence function is completed.
-
Dispatch_barrier_sync Dispatch_barrier_sync is used to submit synchronized execution of fence block tasks. The dispatch_barrier_sync function does not return immediately but only after the submitted tasks are completed. Unlike dispatch_barrier_async, this function is executed synchronously and does not return until the fence task has completed, so all subsequent tasks are blocked.
- Test 1:
- (void)barrierSync
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"Task 1 -- %@", [NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
NSLog(@"dispatch_barrier_sync -- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"Task 2 -- %@", [NSThread currentThread]);
});
NSLog(@"end -- %@", [NSThread currentThread]);
}
Copy the code
- Test 2
- (void)barrierAsync
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"Task 1 -- %@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async -- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"Task 2 -- %@", [NSThread currentThread]);
});
NSLog(@"end -- %@", [NSThread currentThread]);
}
Copy the code
Test 1
Test 2
dispatch_barrier_sync
与dispatch_barrier_async
Common ground:
- Waits for tasks inserted in the queue in front of it (
Task the begin
Task 1
) Execute first. - Will be waiting for their own task (
barrier operation
) Perform subsequent tasks after completion (Task 2
Task end
).
dispatch_barrier_sync
与dispatch_barrier_async
The difference between:
- When inserting a task into the queue,
dispatch_barrier_sync
Need to wait for their tasks (barrier operation
) after the execution is complete, subsequent tasks are inserted (Task 2
Task end
), and then perform the following tasks. soTask end
比barrier operation
After printing. In plain English, it blocks the thread. dispatch_barrier_async
After inserting its task into the queue, it does not wait for the result of its barrier operation and continues to insert subsequent tasks. soTask end
To print first.
- Test 3:
Dispatch_barrier_sync thread
与Task thread
If it’s inconsistent, what happens?
- (void)barrierSync
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT); Dispatch_queue_t queue2 = dispatch_queue_create("com.akironer2", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"Task 1 -- %@", [NSThread currentThread]); }); // block the new thread dispatch_barrier_sync(queue2, ^{NSLog(@)"dispatch_barrier_sync -- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"Task 2 -- %@", [NSThread currentThread]);
});
NSLog(@"end -- %@", [NSThread currentThread]);
}
Copy the code
dispatch_barrier_async
- The essence of a barrier is to block threads.
Dispatch_barrier thread
与Task thread
Inconsistent, barrier is not in effect. - Therefore, when we use AFNetworking to make network requests, we cannot use the fence function to block the synchronization lock because AFNetworking has its own queue.
- Test 4: Yes
Global concurrent queue
What is the effect of using the fence function?
- Using a fence function on a global concurrent queue can block other parts of the system that call the global queue and cause it to crash.
5.5 dispatch_group Dispatch group
Application scenario: Execute two time-consuming tasks asynchronously and return to the main thread to execute the tasks after the two time-consuming tasks are completed.
5.5.1 dispatch_group_async & dispatch_group_notify
dispatch_group_async
The task is placed in a queue, and the queue is placed in a queue groupdispatch_group_notify
Monitor the completion status of tasks in the group. When all tasks are completed,Additional tasks
到group
And execute the task.
- (void)groupAsync
{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_group_async(group, queue, ^{
sleep(5);
NSLog(@"Task 1 -- %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"Task 2 -- %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
sleep(4);
NSLog(@"Task 3 -- %@", [NSThread currentThread]);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"end -- %@", [NSThread currentThread]); }); } GCD[2123:330417] begin -- <NSThread: 0x60000212AD40 >{Number = 1, name = main} 22:23:15.182107+0800 GCD[2123:330584] 0x600002150080>{Number = 3, Name = (NULL)} 22:23:16.180985+0800 GCD[2123:330585] 0x60000215C180 >{Number = 4, Name = (NULL)} 22:23:17.182406+0800 GCD[2123:330596] 0x600002122EC0 >{Number = 5, name = (null)} 22:23:17.182667+0800 GCD[2123:330596] end -- <NSThread: 0x600002122ec0>{number = 5, name = (null)}Copy the code
5.5.2 dispatch_group_wait
Suspends the current thread and waits for tasks in the specified group to complete before proceeding. Compared with dispatch_group_notify, dispatch_group_wait blocks the thread.
- (void)groupWait
{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_group_async(group, queue, ^{
sleep(5);
NSLog(@"Task 1 -- %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"Task 2 -- %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
sleep(4);
NSLog(@"Task 3 -- %@", [NSThread currentThread]);
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"end -- %@", [NSThread currentThread]); } GCD[2157:336097] begin -- <NSThread: 0x6000001D6D00 >{number = 1, name = main} 22:31:48.392504+0800 GCD[2157:336257] 0x6000001A4640 >{Number = 6, name = (null)} 22:31:49.392455+0800 GCD[2157:336255] 0x6000001dc400>{Number = 5, name = (null)} 22:31:50.395577+0800 GCD[2157:336256] 0x6000001AC380 >{Number = 4, name = (null)} 22:31:50.395978+0800 GCD[2157:336256] end -- <NSThread: 0x6000001ac380>{number = 4, name = (null)}Copy the code
5.5.3 dispatch_group_enter & dispatch_group_leave
dispatch_group_enter
It indicates that a task is appended to the group and executed once, which is equivalent to the number of unfinished tasks in the group +1dispatch_group_leave
It indicates that a task has left the group and is executed once, which is equal to the number of unfinished tasks in the group -1.- This parameter is enabled only when the number of unfinished tasks in the group is 0
dispatch_group_wait
Unblock, and append todispatch_group_notify
Task in.
- (void)groupWait
{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
sleep(5);
NSLog(@"Task 1 -- %@", [NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"Task 2 -- %@", [NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"end -- %@", [NSThread currentThread]); }); } 22:42:10.338809+0800 GCD[2188:343124] begin -- <NSThread: 0x60000338ADC0 >{Number = 1, name = main} 22:42:13.340545+0800 GCD[2188:343288] 0x6000033E8340 >{Number = 5, Name = (null)} 22:42:15.343788+0800 GCD[2188:343293] task 1 -- <NSThread: 0x600003304000>{Number = 7, Name = (null)} 22:42:15.344152+0800 GCD[2188:343293] end -- <NSThread: 0x600003304000>{number = 7, name = (null)}Copy the code
5.6 Dispatch_semaphore Semaphore
Application Scenarios:
- Synchronize threads to convert asynchronous tasks to synchronous tasks
- Keep the thread safe and lock the thread.
A semaphore is a counter – based multithreaded synchronization mechanism used to manage concurrent access to resources. The semaphore has an internal value that can increase or decrease atomically. If an action tries to reduce the value of the semaphore to less than zero, the action will be blocked until another caller (in another thread) increases the value of the semaphore.
dispatch_semaphore_create
: Creates a Semaphore and initializes the total amount of signalsdispatch_semaphore_signal
: Sends a signal that increases the total number of signals by 1dispatch_semaphore_wait
: Can reduce the total semaphore by 1. A semaphore less than or equal to 0 blocks the current thread until the semaphore is greater than 0 or passes the input time value; If the semaphore is greater than 0, the semaphore is reduced by 1 and returned, and the program continues to execute.
The code executed between dispatch_semaphore_WAIT and dispatch_semaphore_signal only allows a limited number of threads to enter at a time, effectively ensuring that only a limited number of threads can enter in a multi-threaded environment.
Note: Semaphore use starts with figuring out which thread you want to handle waiting (blocking) and which thread to continue executing, and then using the semaphore.
5.6.1 Dispatch_Semaphore Thread Synchronization
- (void)semaphoreAync
{
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%d -- thread %@", i, [NSThread currentThread]); // After the printing task is complete, the dispatch_semaphore_signal(sem) is unlocked. }); // dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); }}Copy the code
- Semaphore was originally created with a count of 0.
- Asynchronous execution will
Task 1
After append to the queue, execute without waitingdispatch_semaphore_wait
, semaphore minus 1, semaphore == -1, the current thread is blocked. Task 1
Start executing. Perform todispatch_semaphore_signal
After that, the total semaphore increases by 1, semaphore == 0, and the blocked thread resumes execution.
5.6.2 Dispatch_Semaphore Thread Security
- (void)saleTickets
{
NSLog(@"begin -- %@", [NSThread currentThread]); self.ticketCount = 20; Dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL); 2 dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketsSemaphore];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketsSemaphore];
});
}
- (void)saleTicketsSemaphore
{
while{// dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);if (self.ticketCount > 0) {
self.ticketCount --;
NSLog(@"Remaining votes: %ld Window: %@", (long)self.ticketCount, [NSThread currentThread]); Sleep (0.1); }else {
NSLog(@"Sold out %@", [NSThread currentThread]); // Unlock dispatch_semaphore_signal(self.semaphore);break;
}
// 相当于解锁
dispatch_semaphore_signal(self.semaphore);
}
}
Copy the code
5.6.3 Maximum dispatch_semaphore concurrency
- (void)dispatchAsyncLimit { dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); Dispatch_async (queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"Task 1 execution --%@", [NSThread currentThread]);
sleep(1);
NSLog(@"Task 1 completed --%@", [NSThread currentThread]); dispatch_semaphore_signal(semaphore); }); Dispatch_async (queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"Task 2 execution --%@", [NSThread currentThread]);
sleep(1);
NSLog(@"Task 2 completed --%@", [NSThread currentThread]); dispatch_semaphore_signal(semaphore); }); Dispatch_async (queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"Task 3 execution --%@", [NSThread currentThread]);
sleep(1);
NSLog(@"Task 3 completed --%@", [NSThread currentThread]); dispatch_semaphore_signal(semaphore); }); }! [](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/4/26/171b23c361a2d077~tplv-t2oaga2asx-image.image )Copy the code
5.7 dispatch_source
Application scenario: GCDTimer
NSTimer relies on Runloop, which can run in different modes. If Runloop is blocked, NSTimer firing will be delayed until the next Runloop cycle, so NSTimer timing will be incorrect. GCD timers do not rely on Runloop and are much more accurate.
@property (nonatomic, strong) dispatch_source_t timer; Dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); Timer _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); Dispatch_source_set_timer (_timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC); // 4. Set timer event callback dispatch_source_set_event_handler(_timer, ^{NSLog(@)"GCDTimer"); }); // 5. By default, dispatch_resume(_timer) needs to be activated.Copy the code
The resources
Official document — Dispatch
Full court drunk three thousand guests – fence function