exercises
- (void)viewDidLoad {
[super viewDidLoad];
__block int a = 0;
while (a < 5) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
a++;
});
}
NSLog(@"a = %d", a);
}
Copy the code
A, A = 0 B, A < 5 C, A = 5 D, A > 5
A, b, C, D
The analysis of
- The code inside the while loop is an asynchronous function, and the entire code block is a time-consuming operation, so it does not wait until the entire code is executed before entering the next loop, but directly enters the next loop
- A block of code is executed each time, and since it is an asynchronous function, a new thread is opened each time to execute the task
- The initial value of a is 0, a is always 0 before dispatch_async is executed, and a++ is executed many times
- When a++ is executed for the first time, a = 1. When the value of A is changed, because there is only one address of A, that is, the same memory space is changed, so the value of a in other threads will be changed
- When a is greater than 5 for the first time, it exits the loop, but there are many other threads whose A ++ has not yet been fully executed, so it continues to increase after exiting the loop
- So a will print something greater than or equal to 5.
Print result:
Seeing the printed result obviously wastes a lot of performance, and there is a risk of trying to hit the correct result directly. To address this risk we can try using semaphores
// When value equals 1, the lock is executed one at a time. dispatch_semaphore_t sem = dispatch_semaphore_create(1); // dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER) / / unlock dispatch_semaphore_signal (sem);Copy the code
Optimize the code
- (void)viewDidLoad { [super viewDidLoad]; //dispatch_semaphore_create(long value); When value equals 1, the lock is executed one at a time. dispatch_semaphore_t sem = dispatch_semaphore_create(1); __block int a = 0;while (a < 5) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"A = %d----%@", a, [NSThread currentThread]); a++; dispatch_semaphore_signal(sem); }); // timeout = DISPATCH_TIME_FOREVER (sem, DISPATCH_TIME_FOREVER); // } NSLog(@"a = %d", a);
}
Copy the code
Print result:
The answer to parse
- The purpose of the semaphore is to execute the asynchronous code block every time the while loop is executed
- Since this is an asynchronous code block, the code behind the block will be executed first, and then a new thread will be started to execute the code inside the block. When a++ is finished, the whole code block will be executed.
- So when a block of code is about to be executed, a signal lock is added, and when the whole block of code is meant to be over, it is unlocked again, so that unnecessary performance is not wasted.
The fence
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
Copy the code
It mainly controls the task execution sequence and synchronizes tasks
-
dispatch_barrier_async
We’re not here until we’ve finished our mission
-
dispatch_barrier_sync
The effect is the same, but this will block the thread and affect the execution of later tasks
-
Note that the fence function can only control the same concurrent queue
The basic use
- (void)demo2{
dispatch_queue_t concurrentQueue = dispatch_queue_create("janice", DISPATCH_QUEUE_CONCURRENT); */ dispatch_async(concurrentQueue, ^{sleep(1); NSLog(@"123"); }); /* 2. Dispatch_barrier_async (concurrentQueue, ^{NSLog(@)"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - % @ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --",[NSThread currentThread]); }); /* dispatch_async(concurrentQueue, ^{NSLog(@)"Load so much, take a breath!!");
});
NSLog(@"************ dry up!!");
}
Copy the code
Print result:
- Since 1, 2, and 3 are asynchronous functions, I printed ‘Get up and do it’ first.
- Because 2 is a fence function, it doesn’t execute until 1 is played, and 3 doesn’t execute until the fence function is finished.
Actual application Scenario 1
- (void)demo3{
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT); // signal -- thread BUGfor (int i = 0; i<2000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)]; NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; [self.mArray addObject:image]; }); }}Copy the code
Print the result
There is a problem, 2000 images added, but the actual print effect is only 1997, this is why, indicating that the thread is not safe.
Optimally, add a fence when the image is added to the array
- (void)demo3{dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT); // signal -- thread BUGfor (int i = 0; i<2000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)]; NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; dispatch_barrier_async(concurrentQueue, ^{ [self.mArray addObject:image]; }); }); }}Copy the code
Print result:
Add a fence to keep it thread safe
Actual application Scenario 2
Replace custom concurrent queues with global concurrent queues.
- (void)demo3{ dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0); // signal -- thread BUGfor (int i = 0; i<2000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)]; NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; dispatch_barrier_async(concurrentQueue, ^{ [self.mArray addObject:image]; }); }); }}Copy the code
When using the fence function, you must pay attention to it, it must be a custom concurrent queue, global queue is used by the whole system, in which the situation will cause congestion, so it will crash
Conclusion:
- The fence function has a synchronization effect
- The fence function can also play a security role
- Blocking the queue
- Use your own queue rather than global queues when blocking them
- It only works if you use the same queue
Scheduling group
Function: Controls the execution sequence of tasks
- Dispatch_group_create create a group
- Dispatch_group_async Group task
- Dispatch_group_notify Notifies of group task completion
- Dispatch_group_wait Time for executing group tasks
- Dispatch_group_enter into groups
- Dispatch_group_leave out group
- Pay attention to use in combination
The basic use
- (void)groupDemo{// Create a dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_queue_t queue1 = dispatch_queue_create("com.lg.cn", DISPATCH_QUEUE_CONCURRENT);
// SIGNAL
dispatch_group_async(group, queue, ^{
NSString *logoStr = @"http://p.qpic.cn/qqcourse/QFzQYCgCrxlq7n5Jats36WGb4wxzJIYmFplnUUgdxk4gy66xicbmGCDM5DXDuVNQP/";
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]];
UIImage *image = [UIImage imageWithData:data];
[self.mArray addObject:image];
});
dispatch_group_async(group, queue1, ^{
sleep(2);
NSString *logoStr = @"https://www.baidu.com/img/[email protected]";
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]];
UIImage *image = [UIImage imageWithData:data];
[self.mArray addObject:image];
});
__block UIImage *newImage = nil;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"Number of arrays :%ld",self.mArray.count);
for (int i = 0; i<self.mArray.count; i++) {
UIImage *waterImage = self.mArray[i];
newImage = [KC_ImageTool kc_WaterImageWithWaterImage:waterImage backImage:newImage waterImageRect:CGRectMake(20, 100*(i+1), 100, 40)];
}
self.imageView.image = newImage;
});
}
Copy the code
A semaphore
dispatch_semaphore_t
Copy the code
- Dispatch_semaphore_create Creates a semaphore
- Dispatch_semaphore_wait Semaphore wait
- Dispatch_semaphore_signal Indicates signal release
- Controls the number of concurrent GCDS
The basic use
- (void)viewDidLoad { [super viewDidLoad]; dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // Semaphore -- GCD controls concurrency // synchronization // summary: The value of dispatch_semapHORE_t semaphore = dispatch_semaphoRE_create (1). The value of dispatch_semaphore_t semaphore = dispatch_semaphore_create(1). Dispatch_async (queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"Mission 1");
sleep(1);
NSLog(@"Task 1 completed."); dispatch_semaphore_signal(semaphore); }); Dispatch_async (queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"Mission 2");
sleep(1);
NSLog(@"Mission 2 completed."); dispatch_semaphore_signal(semaphore); }); Dispatch_async (queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"Mission 3");
sleep(1);
NSLog(@"Mission 3 completed.");
dispatch_semaphore_signal(semaphore);
});
}
Copy the code
Print result:
dispatch_source
- Its CPU load is very small and does not occupy resources as much as possible
- Advantages of connectivity
When one of its functions dispatch_source_merge_data is called on any thread, the dispatch_source_merge_data will execute the Dispatch Source implementation’s defined handle (which can be simply understood as a block). Is the type of event that a Dispatch source supports.
A handle is a pointer to a pointer that points to a class or structure that has a close relationship with the system
HINSTANCE (instance handle), HBITMAP (bitmap handle), HDC (device representation handle), HICON (icon handle), etc. There is also a generic HANDLE called HANDLE, as in the following statement:
- Dispatch_source_create create the source
- Dispatch_source_set_event_handler Sets the callback of source events
- Dispatch_source_merge_data Source event setting data
- Dispatch_source_get_data Obtains source event data
- Dispatch_resume continue
- Dispatch_suspend hang
The basic use
ViewController.m
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (nonatomic, strong) dispatch_source_t source;
@property (nonatomic, strong) dispatch_queue_t queue;
@property (nonatomic, assign) NSUInteger totalComplete;
@property (nonatomic) BOOL isRunning;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.totalComplete = 0;
self.queue = dispatch_queue_create("com.tz.cn.janice", 0); The first parameter: dispatch_source_type_ttypeTo set the type of GCD source methods, we have listed above. Uintptr_t handle Apple uintptr_t handle Unsigned long mask Apple's API says that using DISPATCH_TIMER_STRICT will result in increased power consumption. After all, precise time is required, so it is normal to send 0, depending on the business situation. The fourth parameter dispatch_queue_t _Nullable queue specifies the queue to which the timer event processing blocks are submitted. Can pass Null, default is global queue. Note: When submitting to the global queue, the time processing callback needs to asynchronously fetch the UI thread, update the UI... But this seems to be common sense, again... */ self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue()); Call dispatch_source_set_cancel_handler(dispatch_source_set_cancel_handler)source// encapsulate the trigger function we need to call -- response dispatch_source_set_event_handler(self.source, ^{ NSUInteger value = dispatch_source_get_data(self.source); Self. totalComplete += value; NSLog(@"Progress: %.2f", the self. TotalComplete / 100.0); Self. ProgressView. Progress = self. TotalComplete / 100.0; }); self.isRunning = YES; dispatch_resume(self.source); - (IBAction)didClickStartOrPauseAction:(id)sender {if(self.isrunning) {// Dispatch_suspend (self.source); dispatch_suspend(self.queue); // MainQueue suspends self.isRunning = NO; [sendersetTitle:@"Paused..." forState:UIControlStateNormal];
}else{
dispatch_resume(self.source);
dispatch_resume(self.queue);
self.isRunning = YES;
[sender setTitle:@"Loading..." forState:UIControlStateNormal];
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"Click to start loading");
for (NSUInteger index = 0; index < 100; index++) {
dispatch_async(self.queue, ^{
if(! self.isRunning) { NSLog(@"Pause download");
return ;
}
sleep(2);
dispatch_source_merge_data(self.source, 1); // sourceValue response}); }}Copy the code
Print result: