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: