当前位置:网站首页>[OC study notes]gcd review
[OC study notes]gcd review
2022-07-20 05:47:00 【Billy Miracle】
of GCD Related content of , I mentioned it before :[OC Learning notes ] On learning GCD Thread related content 、[OC Learning notes ]Grand Central Dispatch.
GCD Communication between threads
In the ordinary development process , We usually do it in the main thread UI Refresh , Then usually put some time-consuming operations on other threads , For example, picture download 、 File upload, etc . And when we do time-consuming operations in other threads sometimes , Need to go back to the main thread , So we use the communication between threads .
/** * Inter thread communication */
- (void)communication {
// Get global concurrent queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// Get home line
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// Add tasks asynchronously 1
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"1---%@",[NSThread currentThread]); // Print current thread
// Back to the main thread
dispatch_async(mainQueue, ^{
// Appending tasks executed in the main thread
NSLog(@"2---%@",[NSThread currentThread]); // Print current thread
});
});
NSLog(@"3---%@",[NSThread currentThread]); // Print current thread
}
GCD Other methods
GCD Fence method :dispatch_barrier_async
Sometimes we need to perform two sets of operations asynchronously , And after the first set of operations , To start the second set of operations . So we need an equivalent of fence The same method splits two groups of asynchronous operations , Of course, the operation group here can contain one or more tasks . And that's where it comes in dispatch_barrier_async
Methods a fence was formed between the two operation groups .dispatch_barrier_async
Method will wait until all the tasks added to the concurrent queue have been executed , Then append the specified task to the asynchronous queue . And then in dispatch_barrier_async
After the task added by the method is executed , The asynchronous queue is restored to the normal action , Then append the task to the asynchronous queue and start executing .
/** * Fence method dispatch_barrier_async */
- (void)barrier {
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// Additional tasks 1
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"1---%@",[NSThread currentThread]); // Print current thread
});
dispatch_async(queue, ^{
// Additional tasks 2
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"2---%@",[NSThread currentThread]); // Print current thread
});
dispatch_barrier_async(queue, ^{
// Additional tasks barrier
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"barrier---%@",[NSThread currentThread]);// Print current thread
});
dispatch_async(queue, ^{
// Additional tasks 3
[NSThread sleepForTimeInterval:1]; // Simulation time operation
NSLog(@"3---%@",[NSThread currentThread]); // Print current thread
});
dispatch_async(queue, ^{
// Additional tasks 4
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"4---%@",[NSThread currentThread]); // Print current thread
});
}
Output :
stay dispatch_barrier_async
As can be seen from the execution results : After performing the operation in front of the fence , Before executing the fence operation , Finally, perform the operation behind the fence .
GCD Delay execution method :dispatch_after
We often meet such demands : At the appointed time ( for example 3 second ) Then perform a task . It can be used GCD Of dispatch_after
Method to implement .
It should be noted that :dispatch_after
Method does not start processing after a specified time , Instead, append the task to the home queue after the specified time . Strictly speaking , This time is not absolutely accurate , But want to roughly delay the execution of the task ,dispatch_after
The method is very effective .
/** * Delay execution method dispatch_after */
- (void)after {
NSLog(@"currentThread---%@",[NSThread currentThread]); // Print current thread
NSLog(@"asyncMain---begin");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2 Seconds later, add the task code to the main queue asynchronously , And start execution
NSLog(@"after---%@",[NSThread currentThread]); // Print current thread
});
}
GCD One time code ( Only once ):dispatch_once
We are creating a singleton 、 Or when there is code that is only executed once during the whole program running , We used GCD Of dispatch_once
Method . Use dispatch_once
Method can ensure that a piece of code is only executed during program operation 1 Time , And even in a multithreaded environment ,dispatch_once
It can also guarantee thread safety .
/** * One time code ( Only once )dispatch_once */
- (void)once {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Only execute 1 Code for the next time ( It's thread safe by default )
});
}
GCD Fast iteration method :dispatch_apply
Usually we use them for
Loop through , however GCD It provides us with a fast iterative method dispatch_apply
.dispatch_apply
Append the specified task to the specified queue according to the specified number of times , And wait for the end of all queue execution .
If it is used in a serial queue dispatch_apply
, Well then for
The cycle is the same , Synchronous execution in sequence . But this does not reflect this “ Fast iteration ” That's the meaning of .
We can use concurrent queues for asynchronous execution . For example, traversal 0~9 this 6 A digital ,for
The way to loop is to take out one element at a time , One by one .dispatch_apply
It can be used in multiple threads at the same time ( asynchronous ) Traverse multiple numbers .
And a little bit more , Whether in a serial queue , Or in the concurrent queue ,dispatch_apply
Will wait for all tasks to be completed , This is like synchronous operation , It's also like... In the queue group dispatch_group_wait
Method .
/** * Fast iteration method dispatch_apply */
- (void)apply {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"apply---begin");
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");
}
Because the task is executed asynchronously in the concurrent queue , So the execution time of each task is variable , The order of execution is not necessarily . however apply—end Must be executed at the end . This is because dispatch_apply
Method will wait for all tasks to complete .
GCD Queue group :dispatch_group
Sometimes we have such needs : Execute asynchronously 2 It's a time-consuming task , Then when 2 After all the time-consuming tasks are completed, return to the main thread to execute the task ( For example, refresh after obtaining several data at the same time UI). At this time, we can use GCD Queue group for .
- Call the... Of the queue group
dispatch_group_async
First put the task in the queue , Then put the queue into the queue group . Or use queue groupdispatch_group_enter
、dispatch_group_leave
Combine to achievedispatch_group_async
. - Call the... Of the queue group
dispatch_group_notify
Return to the specified thread to execute the task . Or usedispatch_group_wait
Return to the current thread and continue to execute downward ( Will block the current thread ).
dispatch_group_notify
monitor group
The completion status of the task in , When all the tasks are done , Append task to group
in , And perform the task .
/** * Queue group dispatch_group_notify */
- (void)groupNotify {
NSLog(@"currentThread---%@",[NSThread currentThread]); // Print current thread
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Additional tasks 1
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"1---%@",[NSThread currentThread]); // Print current thread
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Additional tasks 2
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"2---%@",[NSThread currentThread]); // Print current thread
});
...
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// After the previous asynchronous tasks are executed , Return to the main thread to execute the next task
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"6---%@",[NSThread currentThread]); // Print current thread
NSLog(@"group---end");
});
}
When all tasks are completed , To perform dispatch_group_notify
relevant block
The task .
dispatch_group_wait
Pause current thread ( Block the current thread ), Wait for the designated group
After the task execution in , Will continue to execute .
/** * Queue group dispatch_group_wait */
- (void)groupWait {
NSLog(@"currentThread---%@",[NSThread currentThread]); // Print current thread
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Additional tasks 1
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"1---%@",[NSThread currentThread]); // Print current thread
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Additional tasks 2
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"2---%@",[NSThread currentThread]); // Print current thread
});
...
// Wait until all the above tasks are completed , Will continue to execute ( Will block the current thread )
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"group---end");
}
When all tasks are completed , To perform dispatch_group_wait
Later operations . however , Use dispatch_group_wait
Will block the current thread .
dispatch_group_enter、dispatch_group_leave
dispatch_group_enter
Indicates that a task is added togroup
, Do it once , amount togroup
Number of unfinished tasks in +1dispatch_group_leave
Marks the departure of a missiongroup
, Do it once , amount togroup
Number of unfinished tasks in -1.- When
group
The number of unfinished tasks in is 0 When , To makedispatch_group_wait
unblocked , And execute append todispatch_group_notify
The task .
/** * Queue group dispatch_group_enter、dispatch_group_leave */
- (void)groupEnterAndLeave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // Print current thread
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
// Additional tasks 1
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"1---%@",[NSThread currentThread]); // Print current thread
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
// Additional tasks 2
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"2---%@",[NSThread currentThread]); // Print current thread
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Wait until all the previous asynchronous operations are completed , Back to the main thread .
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"3---%@",[NSThread currentThread]); // Print current thread
NSLog(@"group---end");
});
}
When all tasks are completed , To perform dispatch_group_notify
The task . there dispatch_group_enter
、dispatch_group_leave
Combine , In fact, it is equivalent to dispatch_group_async
.
GCD Semaphore :dispatch_semaphore
GCD Semaphores in refer to Dispatch Semaphore, Is the signal holding the count . Similar to the railings of highway toll stations . When you can pass , Open the railing , When you can't pass , Close the railing . stay Dispatch Semaphore in , Use counting to complete this function , Count less than 0 When you need to wait , Not through . Count as 0 Or greater than 0 when , Don't wait to pass . The count is greater than 0 And the count decreases 1 Don't wait , It can be done by .
Dispatch Semaphore There are three ways :
dispatch_semaphore_create
: Create a Semaphore And initialize the total amount of signalsdispatch_semaphore_signal
: Send a signal , Add... To the total signal 1dispatch_semaphore_wait
: It can reduce the total semaphore 1, The incoming semaphore is less than or equal to 0 I'll wait all the time ( No return , That is, blocking the thread ), Otherwise, it can be executed normally .
The use premise of semaphore is : Figure out which thread you need to handle waiting ( Blocking ), Which thread will continue to execute , Then use semaphores .
Dispatch Semaphore In actual development, it is mainly used for :
- Keep threads synchronized , Convert asynchronous execution tasks to synchronous execution tasks
- Ensure thread safety , Lock the thread
Dispatch Semaphore Thread synchronization
We are in development , There will be such a demand : Perform time-consuming tasks asynchronously , And use the results of asynchronous execution for some extra operations . let me put it another way , amount to , Convert asynchronous execution tasks to synchronous execution tasks . for instance :AFNetworking in AFURLSessionManager.m
Inside tasksForKeyPath:
Method . By introducing semaphores , Wait for the result of asynchronously executing the task , Get tasks, And then back to the tasks.
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
tasks = dataTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
below , Let's use Dispatch Semaphore Thread synchronization , Convert asynchronous execution tasks to synchronous execution tasks .
/** * semaphore Thread synchronization */
- (void)semaphoreSync {
NSLog(@"currentThread---%@",[NSThread currentThread]); // Print current thread
NSLog(@"semaphore---begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int number = 0;
dispatch_async(queue, ^{
// Additional tasks 1
[NSThread sleepForTimeInterval:2]; // Simulation time operation
NSLog(@"1---%@",[NSThread currentThread]); // Print current thread
number = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end,number = %zd",number);
}
from Dispatch Semaphore The code for thread synchronization can be seen :semaphore---end
It's the end of execution number = 100;
It was printed later . And output results number
by 100. This is because asynchronous execution does not do any waiting , You can go on with the task .
Execution order as follows :
- semaphore The initial creation count is 0.
- Asynchronous execution take Mission 1 Append to queue , No waiting , Then perform
dispatch_semaphore_wait
Method ,semaphore
reduce 1, heresemaphore == -1
, The current thread is waiting . - then , Asynchronous task 1 Start execution . Mission 1 Execute to
dispatch_semaphore_signal
after , Total semaphore plus 1, heresemaphore == 0
, The thread being blocked ( The main thread ) Resume to carry on . - Finally print
semaphore---end,number = 100
.
In this way, thread synchronization is realized , Convert asynchronous execution tasks to synchronous execution tasks .
Dispatch Semaphore Thread safety and thread synchronization ( Lock the thread )
Thread safety
: If your code is in a process where multiple threads are running at the same time , These threads may run the code at the same time . If the result of each run is the same as that of single thread run , And the values of other variables are the same as expected , It's thread safe .
If global variables in each thread 、 Static variables are read only , No write operation , Generally speaking , This global variable is thread safe ; If there are multiple threads performing write operations at the same time ( Change variables ), In general, we need to consider thread synchronization , Otherwise, thread safety may be affected .
Thread synchronization
: It can be understood as thread A and Threads B Together with ,A To a certain extent, it depends on threads B Some result of , So stop , schematic B function ;B According to the word , Give the result to A;A Continue to operate .
A simple example is : Two people chat together . Two people cannot talk at the same time , Avoid not hearing clearly ( Operation conflict ). Wait for one person to finish ( A thread ends the operation ), Another one ( Another thread starts again ).
Let's use the example of ticket sales to illustrate :
Non-thread safety ( Don't use semaphore)
/** * Non-thread safety : Don't use semaphore * Initialize the number of tickets 、 Ticket window ( Non-thread safety )、 And start selling tickets */
- (void)initTicketStatusNotSafe {
NSLog(@"currentThread---%@",[NSThread currentThread]); // Print current thread
self.ticketSurplusCount = 50;
// queue1 On behalf of the ticket window 1
dispatch_queue_t queue1 = dispatch_queue_create("net.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 On behalf of the ticket window 2
dispatch_queue_t queue2 = dispatch_queue_create("net.testQueue2", DISPATCH_QUEUE_SERIAL);
// queue3 On behalf of the ticket window 3
dispatch_queue_t queue3 = dispatch_queue_create("net.testQueue3", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketNotSafe];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketNotSafe];
});
dispatch_async(queue3, ^{
[weakSelf saleTicketNotSafe];
});
}
/** * Sell ticket ( Non-thread safety ) */
- (void)saleTicketNotSafe {
while (1) {
if (self.ticketSurplusCount > 0) {
// If there are still tickets , Continue to sell
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@" The number of votes left :%d window :%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else {
// If it's sold out , Close the ticket window
NSLog(@" All the train tickets are sold out ");
break;
}
}
}
Observations , You can find that some tickets have been sold several times at different windows , This is obviously unscientific , There was a conflict :
Thread safety ( Use semaphore Lock )
/** * Thread safety : Use semaphore Lock * Initialize the number of tickets 、 Ticket window ( Thread safety )、 And start selling tickets */
- (void)initTicketStatusSafe {
NSLog(@"currentThread---%@",[NSThread currentThread]); // Print current thread
NSLog(@"semaphore---begin");
semaphoreLock = dispatch_semaphore_create(1);
self.ticketSurplusCount = 50;
// queue1 On behalf of the ticket window 1
dispatch_queue_t queue1 = dispatch_queue_create("net.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 On behalf of the ticket window 2
dispatch_queue_t queue2 = dispatch_queue_create("net.testQueue2", DISPATCH_QUEUE_SERIAL);
// queue3 On behalf of the ticket window 3
dispatch_queue_t queue3 = dispatch_queue_create("net.testQueue3", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketSafe];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketSafe];
});
dispatch_async(queue3, ^{
[weakSelf saleTicketSafe];
});
}
/** * Selling train tickets ( Thread safety ) */
- (void)saleTicketSafe {
while (1) {
// It's equivalent to locking
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.ticketSurplusCount > 0) {
// If there are still tickets , Continue to sell
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@" The number of votes left :%d window :%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else {
// If it's sold out , Close the ticket window
NSLog(@" All the train tickets are sold out ");
// It's like unlocking
dispatch_semaphore_signal(semaphoreLock);
break;
}
// It's like unlocking
dispatch_semaphore_signal(semaphoreLock);
}
}
thus , There will be no confusion . Just understand semaphore Principle , I know why . The first ticket window uses dispatch_semaphore_wait
Reduce the semaphore 1 be equal to 0( Lock ), Other windows are blocked . After the ticket sale at the first window , Use dispatch_semaphore_signal
, Semaphore plus 1( Unlock ).
dispatch_source
Application scenarios :GCDTimerNSTimer
It's dependence Runloop Of , and Runloop Can run in different modes . If Runloop In a blocked state ,NSTimer
The trigger time will be postponed to the next Runloop cycle , therefore NSTimer
There will be errors in timing . and GCD The timer does not depend on Runloop
, The timing accuracy is much higher .
// To hold strongly
@property (nonatomic, strong) dispatch_source_t timer;
- (void)creatTimer {
__block int time = 0;
// 1. Create a queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2. establish timer
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 3. Set up timer First execution time , interval , accuracy
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
// 4. Set up timer Event callback
dispatch_source_set_event_handler(_timer, ^{
time += 2;
if (time >= 10) {
// 10 Shutdown in seconds timer
dispatch_suspend(self->_timer);
}
NSLog(@"GCDTimer");
});
// 5. The default is suspend state , You need to activate... Manually
dispatch_resume(_timer);
}
边栏推荐
- Pinecone trip x starrocks: the practical road of the new paradigm of real-time data warehouse
- B树-插入
- The practice of software R & D should start from the design
- Report control fastreport Net tutorial: how to use report components in Visual Studio
- 关于extern声明&定义
- 互联网大厂智能座舱斗法
- vim编辑器介绍、3种工作模式
- 数据链路层封装技术、GRE及实验(eNSP)
- 11. Container with the most water
- Cloudwego's design practice in the background platform of flybook management
猜你喜欢
About extern declaration & definition
Intelligent cockpit fighting method of Internet manufacturers
College sales turned to software testing, from 4K in the third line to 20k in the first line. I pushed another door open
Cloudwego's design practice in the background platform of flybook management
便携电源快充方案 30W自动升降压PD快充
Evaluate STR expression results
The trajectory of snakes
[software testing] how to learn mobile terminal testing? Super detailed app testing strategy
Microservice architecture | message queue - [common pit] TBC
Working for 3 months, my understanding of testing work
随机推荐
B树-插入
互联网大厂智能座舱斗法
在同花顺网上开户安全吗
They gathered at the 2022 ecug con just for "China's technological power"
1304_FreeRTOS从任务控制块信息的角度尝试裁剪内核节省资源
【obs-studio开源项目从入门到放弃】obs高级输出内存泄露
Is it safe for novice Xiaobai to open Huatai account again?
Note - "sum of Euler functions of factors"
Install harbor on tke
ArkUI框架进度指示器
剑指Offer 42:连续子数组的最大和
适合新手小白的免费编曲软件FL Studio20中文版
Intelligent cockpit fighting method of Internet manufacturers
Unveiling secrets of matrixcube 101 - functions and architecture of matrixcube
A translation JS reverse, call stack simple usage
弱网测试工具—— clumsy
他们齐聚 2022 ECUG Con,只为「中国技术力量」
小白新手再启牛开通华泰账户安全吗?
6-40V输入 固定5V 3.3V输出 1.1A电流 23-5封装
selenium的下拉+无头浏览器