Welcome to the iOS Exploration series.

  • IOS explores the alloc process
  • IOS explores memory alignment &malloc source code
  • IOS explores ISA initialization & pointing analysis
  • IOS exploration class structure analysis
  • IOS explores cache_T analysis
  • IOS explores the nature of methods and the method finding process
  • IOS explores dynamic method resolution and message forwarding mechanisms
  • IOS explores the dyLD loading process briefly
  • The loading process of iOS explore classes
  • IOS explores the loading process of classification and class extension
  • IOS explores isa interview question analysis
  • IOS Explore Runtime interview question analysis
  • IOS explores KVC principles and customization
  • IOS explores KVO principles and customizations
  • IOS explores the principle of multithreading
  • IOS explores multi-threaded GCD applications
  • IOS explores multithreaded GCD underlying analysis
  • IOS explores NSOperation for multithreading
  • IOS explores multi-threaded interview question analysis
  • IOS Explore the locks in iOS
  • IOS explores the full range of blocks to read

Writing in the front

Grand Central Dispatch, GCD, is pure C and offers a lot of powerful functions

Advantages of THE COMMUNIST Party of China

  • GCD is apple’s solution to multi-core parallel computing
  • – GCDS automatically utilize more CPU cores (such as dual core, quad core)
  • GCD automatically manages thread lifecycles (thread creation, task scheduling, thread destruction)
  • The programmer only needs to tell the COMMUNIST party what task it wants to perform, without writing any thread management code

Here’s what we’ll focus on: the core of GCD — adding tasks to queues and specifying the functions that execute them

A, dispatch_block_t

dispatch_block_t block = ^{
    NSLog(@" Basic Use of GCD");
};
dispatch_queue_t queue = dispatch_queue_create("com.Felix".NULL);
dispatch_async(queue, block);
Copy the code

This line of code best captures the core of GCD:

  • dispatch_block_tUse blocks to encapsulate tasks
  • dispatch_queue_tCreate a queue
  • dispatch_asyncAdds the task to the queue

The above code is often written in this form

dispatch_queue_t queue = dispatch_queue_create("com.Felix".NULL);
dispatch_async(queue, ^{
    NSLog(@" Basic Use of GCD");
});
Copy the code

Dispatch_sync & dispatch_async

Multi-threaded execution tasks are divided into dispatch_sync synchronous execution tasks and dispatch_async asynchronous execution tasks:

  • dispatch_syncSynchronous execution
    • You must wait for the current statement to complete before executing the next statement
    • Thread will not be opened
    • Performs the task of the block in the current thread
  • dispatch_asyncAsynchronous execution
    • The next statement can be executed without waiting for the current statement to complete
    • Opens the thread to execute the block task
    • Asynchrony is a byword for multithreading

Third, dispatch_queue_t

Serial queues
And Serial Dispatch Queue)
Concurrent Dispatch Queue

  • Serial queues: Thread execution can only be executed one by one and wait for the previous execution to finish before executing the next one
    • usedispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL)Creating a serial queue
    • You can also usedispatch_queue_create("xxx", NULL)Creating a serial queue (covered at the bottom of GCD)
  • The home side column: Binds to the main thread, a specially processed serial queue in which all tasks are executed
    • usedispatch_get_main_queue()Gets the main queue
  • Concurrent queue: Threads can execute together at the same time, without waiting for the previous one to complete before executing the next task
    • usedispatch_queue_create("xxx", DISPATCH_QUEUE_CONCURRENT);Creating concurrent queues
  • Global queue: Indicates the concurrent queue provided by the system
    • The easiest is to usedispatch_get_global_queue(0, 0)Gets the system-provided concurrent queue
    • The first parameter is the priority enumeration value, which defaults toDISPATCH_QUEUE_PRIORITY_DEFAULT= 0
    • The priorities are in descending orderDISPATCH_QUEUE_PRIORITY_HIGH,DISPATCH_QUEUE_PRIORITY_DEFAULT,DISPATCH_QUEUE_PRIORITY_LOW,DISPATCH_QUEUE_PRIORITY_BACKGROUND

Serial/concurrent and synchronous/asynchronous permutations

The main queue and the global queue are considered separately, and the combined results are subject to the summary table

1. Serial + Synchronization

Tasks are executed one after another without opening up a thread

- (void)test {
    NSLog(@" main thread -%@"[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@" Serial & synchronous thread %d-%@", i, [NSThreadcurrentThread]); }); }} -------------------- The command output is -------------------
      
       {number = 1, name = main}
      
0x600003B64fc0 >{number = 1, name = main}

      
       {number = 1, name = main}
      
/ /... Sequential output-------------------- The command output is -------------------Copy the code

2. Serial + asynchronous

Tasks are executed one after another, opening up threads

- (void)test {
    NSLog(@" main thread -%@"[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@" Serial & asynchronous thread %d-%@", i, [NSThreadcurrentThread]); }); }} -------------------- The command output is -------------------
      
       {number = 1, name = main}
      
0x6000009B8880 >{number = 6, name = (null)}

      
       {number = 6, name = (null)}
      
/ /... Sequential output-------------------- The command output is -------------------Copy the code

3. Concurrency + synchronization

Tasks are executed one after another without opening up a thread

- (void)test {
    NSLog(@" main thread -%@"[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@" Concurrent & synchronous thread %d-%@", i, [NSThreadcurrentThread]); }); }} -------------------- The command output is -------------------
      
       {number = 1, name = main}
      
0x600003B64fc0 >{number = 1, name = main}

      
       {number = 1, name = main}
      
/ /... Sequential output-------------------- The command output is -------------------Copy the code

4. Concurrent + asynchronous

Task out of order, open up thread

- (void)test {
    NSLog(@" main thread -%@"[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@" Concurrent & asynchronous thread %d-%@", i, [NSThreadcurrentThread]); }); }} -------------------- The command output is -------------------
      
       {number = 1, name = main}
      
NSThread: 0x600002A9CA40 >{number = 5, name = (null)}
NSThread: 0x600002ADD3C0 >{number = 4, name = (null)}
/ /... Out-of-order output-------------------- The command output is -------------------Copy the code

Let’s take a look at the use of primary and global queues:

5. Main queue + synchronization

Waiting for each other, causing a deadlock

- (void)test {
    NSLog(@" main thread -%@"[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@" Main queue & synchronous thread %d-%@", i, [NSThreadcurrentThread]); }); }} -------------------- The command output is -------------------
      
       {number = 1, name = main}
      
/ / collapse...-------------------- The command output is -------------------Copy the code

6. Main queue + asynchronous

Tasks are executed one after another without opening up a thread

- (void)test {
    NSLog(@" main thread -%@"[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@" Main queue & asynchronous thread %d-%@", i, [NSThreadcurrentThread]); }); }} -------------------- The command output is -------------------
      
       {number = 1, name = main}
      
NSThread: 0x600001980d40>{number = 1, name = main}

      
       {number = 1, name = main}
      
/ /... Sequential output-------------------- The command output is -------------------Copy the code

7. Global queue + synchronization

Tasks are executed one after another without opening up threads (concurrent + synchronous)

- (void)test {
    NSLog(@" main thread -%@"[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_global_queue(0.0);
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@" Global queue & synchronous thread %d-%@", i, [NSThreadcurrentThread]); }); }} -------------------- The command output is -------------------
      
       {number = 1, name = main}
      
NSThread: 0x60000099C080 >{number = 1, name = main}

      
       {number = 1, name = main}
      
/ /... Sequential output-------------------- The command output is -------------------Copy the code

8. Global queue + asynchronous

Task out of order, open up thread (concurrent + asynchronous)

- (void)test {
    NSLog(@" main thread -%@"[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_global_queue(0.0);
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@" Global queue & asynchronous thread %d-%@", i, [NSThreadcurrentThread]); }); }} -------------------- The command output is -------------------
      
       {number = 1, name = main}
      
NSThread: 0x600001C8eb00 >{number = 3, name = (null)}
NSThread: 0x600001C82B80 >{number = 7, name = (null)}
/ /... Out-of-order output-------------------- The command output is -------------------Copy the code

To sum up:

Executionqueue Serial queues Concurrent queue The home side column Global queue
Synchronous execution Execute sequentially without opening up threads Execute sequentially without opening up threads A deadlock Execute sequentially without opening up threads
Asynchronous execution Execute sequentially to open up threads Out of order execution, open up threads Execute sequentially without opening up threads Out of order execution, open up threads

Four, dispatch_after

Dispatch_after Delays the execution of blocks in a queue

Application scenario: Delay execution of a task on the main queue, such as viewDidload after 1s, prompting an AlertView (delay join queue, not delay execution)

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

Fifth, dispatch_once

Dispatch_once ensures that the code in the block is executed only once during the running of the App

Application scenarios: singleton and method-Swizzling

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // Create singletons, Method swizzled, or other tasks
});
Copy the code

Six, dispatch_apply

Dispatch_apply appends the specified Block to the specified queue for repeated execution and waits until all processing completes — equivalent to a thread-safe for loop

Application scenario: It is used to calculate the size of each control in advance after pulling network data, preventing calculation during drawing and improving the smoothness of form sliding

  • Add to serial queue — execute sequentially
  • Add to main queue – deadlock
  • Add to concurrent queue – out of order
  • Add to global queue – out of order
- (void)test {
    /** param1: indicates the number of repetitions. Param2: indicates the queue to be appended. Param3: Indicates the task to be executed
    dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_SERIAL);
    NSLog(@ "dispatch_apply before");
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"dispatch_apply thread %zu-%@", index, [NSThread currentThread]);
    });
    NSLog(After the @ "dispatch_apply"); } -------------------- The command output is -------------------/ / dispatch_apply before
0x6000019F8D40 >{number = 1, name = main}
/ /... Whether the output is in order depends on serial or concurrent queues
/ / after dispatch_apply-------------------- The command output is -------------------Copy the code

Seven, dispatch_group_t

Dispatch_group_t: scheduling group task execution group, can listening task group, and set up the waiting time

Application scenario: Refresh the page after multiple interface requests

1.dispatch_group_async

Dispatch_group_notify Notification is sent after dispatch_group_async execution is complete

- (void)test {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0.0);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@" Request completed");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@" Request 2 completed");
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@" Refresh page"); }); } -------------------- The command output is -------------------// Request two completed
// Request completed
// Refresh the page-------------------- The command output is -------------------Copy the code

2.dispatch_group_enter & dispatch_group_leave

“Dispatch_group_enter” and “dispatch_group_leave” are deployed in pairs, making the logic of sending and receiving dispatch_group_leave clearer

- (void)test {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0.0);
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@" Request completed");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@" Request 2 completed");
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@" Refresh page"); }); } -------------------- The command output is -------------------// Request two completed
// Request completed
// Refresh the page-------------------- The command output is -------------------Copy the code

Scheduling group to pay attention to the use of collocation, must be advanced group and then out of the group, one cannot be absent

3.dispatch_group_waituse

long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout)

  • Group: scheduling group to be waited
  • Timeout: The timeout period of waiting (i.e. how long to wait)
    • Set toDISPATCH_TIME_NOWIt indicates that the execution of the scheduling group is complete without waiting
    • Set toDISPATCH_TIME_FOREVERThe current scheduling group is blocked until the scheduling group is complete
  • Return value: yeslongtype
    • The return value is 0 — the scheduling group completed its task within the specified time
    • The return value is not 0 — the scheduling group did not complete the task on time within the specified time

Rewrite the above scheduling group code

- (void)test {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"Request completed.");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"Request two completed.");
        dispatch_group_leave(group);
    });
    
    long timeout = dispatch_group_wait(group, DISPATCH_TIME_NOW);
//    long timeout = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//    long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC));
    NSLog(@"timeout=%ld", timeout);
    if (timeout == 0) {
        NSLog(@"Get things done on time");
    } else {
        NSLog(@"Timeout");
    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"Refresh page"); }); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / timeout = 49 / / request a complete / / request / / / / overtime refresh the page -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -------------------Copy the code

Dispatch_barrier_sync & dispatch_barrier_async

Application scenario: Synchronization lock

Execute asynchronous queues concurrently
Task 3
Task 4
Task 4

GCD provides two apis — dispatch_barrier_sync and dispatch_barrier_Async — to group multiple tasks — and then append tasks to the queue after they have finished executing. In short, it is to perform the pre-fence task, then the fence task, and finally the post-fence task

1. Serial queues use the fence function

- (void)test {
    dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_SERIAL);
    
    NSLog(- % @ "@"[NSThread currentThread]);
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@" Delay task for 2s 1 -- %@"[NSThread currentThread]);
    });
    NSLog(@" First end -- %@"[NSThread currentThread]);
    
// dispatch_barrier_async(queue, ^{
/ / NSLog (@ "-- -- -- -- -- -- -- -- -- -- the fence task -- -- -- -- -- -- -- -- -- - % @", [NSThread currentThread]);
/ /});
// NSLog(@" end -- %@", [NSThread currentThread]);
    
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@" Delay task 2 -- %@ for 1s"[NSThread currentThread]);
    });
    NSLog(@" End of second time -- %@"[NSThread currentThread]);
}
Copy the code

Do not use the fence function

Start -- <NSThread: 0x600001068900>{number = 1, name = main} 0x600001068900>{number = 1, name = main} 0x600001068900>{number = 1, name = main} Delay task 1 -- <NSThread: 0x600001025EC0 >{number = 3, name = (null)} Delay task 2 for 1s -- <NSThread: 0x600001025EC0 >{number = 3, name = (null)}Copy the code

Using the fence function

Start -- <NSThread: 0x6000001bcf00>{number = 1, name = main} 0x6000001bcf00>{number = 1, name = main} NSThread: 0x6000001bcf00>{number = 1, name = main} 0x6000001Bcf00 >{number = 1, name = main} Delay task 1 -- <NSThread: 0 x6000001fcf00 > {number = 5, name = (null)} -- -- -- -- -- -- -- -- -- -- the fence task -- -- -- -- -- -- -- -- -- -- < NSThread: 0x6000001Bcf00 >{number = 1, name = main} Task 2 delayed for 1s -- <NSThread: 0x6000001FCF00 >{number = 5, name = (null)}Copy the code

The fence function is used to group tasks in a queue, so we just focus on task 1 and task 2

Conclusion: Due toSerial queues execute asynchronouslyTasks are executed one after another, so there is no point in using the fence function

2. Use the fence function for concurrent queues

- (void)test {
    dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(- % @ "@"[NSThread currentThread]);
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@" Delay task for 2s 1 -- %@"[NSThread currentThread]);
    });
    NSLog(@" First end -- %@"[NSThread currentThread]);
    
// dispatch_barrier_async(queue, ^{
/ / NSLog (@ "-- -- -- -- -- -- -- -- -- -- the fence task -- -- -- -- -- -- -- -- -- - % @", [NSThread currentThread]);
/ /});
// NSLog(@" end -- %@", [NSThread currentThread]);
    
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@" Delay task 2 -- %@ for 1s"[NSThread currentThread]);
    });
    NSLog(@" End of second time -- %@"[NSThread currentThread]);
}
Copy the code

Do not use the fence function

Start -- <NSThread: 0x600002384F00 >{number = 1, name = main} 0x600002384F00 >{number = 1, name = main} 0x600002384F00 >{number = 1, name = main} Delay task 2 for 1s -- <NSThread: 0x6000023EC300 >{number = 5, name = (null)} Delay 2s task 1 -- <NSThread: 0x60000238C180 >{number = 7, name = (null)}Copy the code

Using the fence function

Start -- <NSThread: 0x600000820bc0>{number = 1, name = main} <NSThread: 0x600000820bc0>{number = 1, name = main} 0x600000820BC0 >{number = 1, name = main} Delay task 1 -- <NSThread: 0 x600000863c80 > {number = 4, name = (null)} -- -- -- -- -- -- -- -- -- -- the fence task -- -- -- -- -- -- -- -- -- -- < NSThread: 0x600000863C80 >{number = 4, name = (null)} Delay task 2 for 1s -- <NSThread: 0x600000863C80 >{number = 4, name = (null)}Copy the code

Conclusion: Due toConcurrent queues execute asynchronouslyTasks are executed out of order, so the fence function can be used to control the order of tasks in the queue

3. Dispatch_barrier_sync/dispatch_barrier_async difference

  • dispatch_barrier_async: Will come here after the previous mission is completed
  • dispatch_barrier_sync: has the same effect, but this will block the thread, affecting the execution of later tasks

Change dispatch_barrier_async to dispatch_barrier_sync in case 2

Start -- <NSThread: 0x600001040D40 >{number = 1, name = main} 0x600001040D40 >{number = 1, name = main} Delay task 1 -- <NSThread: 0 x60000100ce40 > {number = 6, name = (null)} -- -- -- -- -- -- -- -- -- -- the fence task -- -- -- -- -- -- -- -- -- -- < NSThread: 0x600001040D40 >{number = 1, name = main} NSThread: <NSThread: 0x600001040D40 >{number = 1, name = main} 0x600001040D40 >{number = 1, name = main} Task 2 delayed for 1s -- <NSThread: 0x60000100CE40 >{number = 6, name = (null)}Copy the code

Conclusion: Dispatch_barrier_async can control the order in which tasks are executed in queues. Dispatch_barrier_sync blocks not only queue execution but also thread execution (use sparsely).

4. Pay attention to fence function

  1. Use custom concurrent queues whenever possible:
    • useGlobal queueDon’tBarrier functionThe role of
    • useGlobal queueWhen the global queue is blocked, other parts of the system that call the global queue may also be blocked and crash (you are not the only one using this queue).
  2. The fence function can only control the same concurrent queueFor example, when MAKING network requests using AFNetworking, why can’t we use the fence function to block the synchronization because AFNetworking has its own queue

Nine, dispatch_semaphore_t

Application scenario: Control the maximum number of concurrent GCD users when synchronization is locked

  • dispatch_semaphore_create(): Creates a semaphore
  • dispatch_semaphore_wait(): Wait for semaphore, semaphore minus 1. When a semaphore< 0Will block the current thread, depending on the incoming wait time to decide what to do next — ifPermanent waitWill wait untilSignalTo carry it out
  • dispatch_semaphore_signal(): Releases the semaphore, and the semaphore increases by 1. When a semaphore> = 0The code after WAIT is executed

The following code requires the semaphore to be output in order (of course the fence function will do the job)

- (void)test {
    dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@" Current %d---- thread %@", i, [NSThread currentThread]);
        });
        // Use the fence function
        // dispatch_barrier_async(queue, ^{});}}Copy the code

Use the semaphore API for code rewriting

- (void)test {
    // Create semaphore
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@" Current %d---- thread %@", i, [NSThread currentThread]);
            // The semaphore is unlocked after the printing task is complete
            dispatch_semaphore_signal(sem);
        });
        // Because of asynchronous execution, the printing task will be slow, so the semaphore is lockeddispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); }}Copy the code

The output

Current 0---- thread <NSThread: 0x600000C2C000 >{number = 5, name = (null)} Current 1---- thread <NSThread: 0x600000C2C000 >{number = 5, name = (null)} Current 2---- threads <NSThread: 0x600000C2C000 >{number = 5, name = (null)} Current 3---- threads <NSThread: 0x600000C2C000 >{number = 5, name = (null)} Current 4---- threads <NSThread: 0x600000C2C000 >{number = 5, name = (null)} Current 5---- threads <NSThread: 0x600000C2C000 >{number = 5, name = (null)} Current 6---- threads <NSThread: 0x600000C2C000 >{number = 5, name = (null)} Current 7---- threads <NSThread: 0x600000C2C000 >{number = 5, name = (null)} Current 8---- thread <NSThread: 0x600000C2C000 >{number = 5, name = (null)} Current 9---- threads <NSThread: 0x600000C2C000 >{number = 5, name = (null)}Copy the code

What if a value of 1 was passed in when the semaphore was created?

  • i=0Sometimes beforeprintOr it may be sent firstwaitSemaphore -1, butwaitThen a semaphore of zero does not block the thread, so enteri=1
  • i=1Sometimes beforeprintOr it may be sent firstwaitSemaphore -1, butwaitThen a semaphore of -1 blocks the thread and waitssignalCarry on
Current 1---- thread <NSThread: 0x600001448D40 >{number = 3, name = (null)} Current 0---- thread <NSThread: 0x60000140C240 >{Number = 6, name = (null)} Current 2---- threads <NSThread: 0x600001448D40 >{number = 3, name = (null)} Current 3---- threads <NSThread: 0x60000140C240 >{Number = 6, Name = (null)} Current 4---- threads <NSThread: 0x60000140C240 >{Number = 6, name = (null)} Current 5---- threads <NSThread: 0x600001448D40 >{number = 3, name = (null)} Current 6---- threads <NSThread: 0x600001448D40 >{number = 3, name = (null)} Current 7---- threads <NSThread: 0x60000140C240 >{Number = 6, Name = (null)} Current 8---- thread <NSThread: 0x600001448D40 >{number = 3, name = (null)} Current 9---- Threads <NSThread: 0x60000140C240 >{number = 6, name = (null)}Copy the code

Conclusion:

  • Creating a semaphore with an incoming value of 1 can pass twice before blocking
  • When the incoming value is 2, it can pass three times before blocking

Ten, dispatch_source

Application scenario: GCDTimer

1. Definition and use

Dispatch_source is a basic data type that can be used to listen for low-level system events

  • Timer Dispatch Source: Timer event source that generates periodic notifications or callbacks
  • Signal Dispatch Source: Listens for signal event sources to notify when a UNIX signal occurs
  • Descriptor Dispatch Source: listens for file or socket event sources and notifies them when file or socket data changes
  • Process Dispatch Source: Listens for process event sources, notifications of process-related events
  • Mach port Dispatch Source: Listens for Mach port event sources
  • Custom Dispatch Source: Listens for custom event sources

Main APIS used:

  • dispatch_source_create: Creates an event source
  • dispatch_source_set_event_handler: Sets the data source callback
  • dispatch_source_merge_data: Sets the event source data
  • dispatch_source_get_data: Obtains the event source data
  • dispatch_resume: continue to
  • dispatch_suspend: hang
  • dispatch_cancleCancelled:

2. Customize the timer

NSTimer is commonly used in iOS development to handle timing logic, but NSTimer relies on Runloop, which can run in different modes. If NSTimer is added in one mode, the timer hangs up while Runloop is running in another mode; If Runloop is blocked, NSTimer firing is delayed until the next Runloop cycle. As a result, NSTimer has errors in timing and is not particularly accurate, whereas GCD timers do not rely on Runloop and are much more accurate

@property (nonatomic.strong) dispatch_source_t timer;
1. Create a queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/ / 2. To create a timer
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0.0, queue);
//3. Set the first execution time, interval and accuracy of timer
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC.0.1 * NSEC_PER_SEC);
//4. Set the timer event callback
dispatch_source_set_event_handler(_timer, ^{
    NSLog(@"GCDTimer");
});
//5. The state is suspended by default and needs to be activated manually
dispatch_resume(_timer);
Copy the code

Use dispatch_source to customize timers.

  • GCDTimerNeed to beStrong holdOtherwise, it is released immediately out of scope and there is no event callback
  • GCDTimerIt is suspended by default and needs to be activated manually
  • GCDTimerThere is norepeatEncapsulation is required to increase flag bit control
  • GCDTimerIf a circular reference exists, useweak+strongOr call ahead of timedispatch_source_cancelCancel the timer
  • dispatch_resumeanddispatch_suspendThe number of calls needs to be balanced
  • sourceinPending stateIf you want to directly setsource = nilOr recreate itsourceCan cause crashes. The right way is inactiveCut withdispatch_source_cancel(source)Release currentsource

The GCD – API

API instructions
dispatch_sync() Synchronous execution
dispatch_async() Asynchronous execution
dispatch_queue_create() Create a queue
dispatch_get_main_queue() Gets the main queue
dispatch_get_global_queue() Get global queue
dispatch_after() Delay to perform
dispatch_once() One-time execution
dispatch_apply() Submitted to the queue
dispatch_group_create() Creating a Scheduling Group
dispatch_group_async() Execute the group task
dispatch_group_enter()/

dispatch_group_leave()
Add or subtract 1 from the number of unfinished tasks in the scheduling group

(The two functions should be used together)
dispatch_group_wait() Set the wait time (success to 0)
dispatch_barrier_sync() Synchronous fence function
dispatch_barrier_async() Asynchronous fence function
dispatch_group_notify() Listen queue group completes execution
dispatch_semaphore_creat() Create semaphore
dispatch_semaphore_wait() Waiting semaphore
dispatch_semaphore_signal() Release semaphore
dispatch_source_create Create the source
dispatch_source_set_event_handler Set the source event callback
dispatch_source_merge_data Source event sets data
dispatch_source_get_data Get the source event data
dispatch_resume Continue to
dispatch_suspend hang
dispatch_cancle cancel

Write in the back

The next article will explore the underlying principles of GCD, and some of the pitfalls of GCD applications will be supplemented in multi-threaded interview questions