IOS martial arts esoteric article summary

Writing in the front

Multithreading plays an important role in iOS, so this article will take you into her…..

A possible secret Demo for this section

I. Basic concepts and principles

① Threads, processes, and queues

①.1 Definition of thread

  • A thread is the basic execution unit of a process, in which all tasks of a process are executed
  • In order for a process to perform a task, it has to have threads,A process must have at least one thread
  • The program starts with a thread by default, this thread is calledThe main threadorThe UI thread

①.2 Definition of process

  • processIt refers to an application that is running in the system, such as wechat and Alipay app, which are both a process
  • eachprocessEach process runs in its own dedicated and protected memory space
  • The Activity Monitor allows you to view the threads that are started in the MAC system

A process is a container for threads that perform tasks. In iOS, it is a single process development, a process is an APP, the process is independent of each other, such as Alipay, wechat, QQ, etc., these are different processes.

①.3 Relationship and difference between process and thread

  • Address space: sameprocessthethreadThe address space of each process is shared, while the address space of each process is independent
  • Resource ownership: SameprocessWithin thethreadShare resources such as memory, I/O, CPU, etc. of the process, butprocessResources between are independent
  • The relationship between the two is the same asThe relationship between factory and assembly line, the factories are independent from each other, and the assembly line in the factory shares the resources of the factory, that isprocessIt’s like a factory,threadEquivalent to one in a factoryAssembly line
  • aprocessAfter crash, in protected mode will not work against otherprocessMake an impact, but onethreadCollapse of the wholeprocessThey’re all dead, soMultiple processesthanmultithreadingrobust
  • processSwitching consumes large resources and is efficient. So design to frequent switching when usingthreadBetter than theprocess. Also, if simultaneous operations are required and some variables are shared at the same time, onlythreadBut can’t useprocess
  • Execution process: each independentprocessThere is a program run entry, sequential execution sequence, and program entry. butthreadCannot be executed independently, must depend on the application, provided by the applicationthreadExecutive control
  • threadIs the basic unit of processor scheduling, butprocessnot
  • Threads have no address space and are contained in the process address space

May feel that these theoretical knowledge is very abstract, Baidu out of a lot of but are not easy to understand, read the following understanding will be fully understood

①.4 Relationship diagram between process and thread

Can put theiOSTo imagineThe mall.processIt’s in the mallThe store.threadIt was hired by the storeemployees:

  • Independence of processes from one another
    • Milk tea shop can’t see juice shop’s account (can’t access memory of other processes)
    • – Juice shop can no longer use milk tea shop’s boobs (resources are independent between processes)
  • A process must have at least one thread
    • Store must have at least one employee (processes must have at least one thread)
    • Morning opening staff (equivalent to the main thread)
  • A process/thread crash
    • Failure of milk tea shop does not affect failure of juice shop (process crash does not affect other processes)
    • [Bug Mc-10821] – Milk tea shop cashiers quit, causing the shop to not function properly (thread crash causes process to crash)

Mobile development doesn’t have to be single-process, Android is multi-process; IOS is sandbox, which is one of the main reasons why Apple runs smoothly and safely

①.5 Relation between thread and Runloop

  • Runloops correspond to threads one by one– arunloopCorresponding to a corethreadWhy is it core becauserunloopCan be nested, but there can only be one core, and their relationships are stored in a global dictionary
  • Runloop is used to manage threads— When the threadrunloopWhen enabled, the thread will go to sleep after completing the task and will be woken up to execute the task
  • runloopIs created on the first fetch and destroyed at the end of the thread
    • For the main thread,runloopIt is created by default as soon as the program starts
    • For child threads,runloopIt is lazily loaded — it is created only when we are using it, so beware of using timers in child threads:Make sure the runloop for the child thread is created, otherwise the timer will not call back

①.6 Factors affecting task execution speed

The following factors will affect the execution speed of the task:

  • cpuThe scheduling
  • The execution rate of the thread
  • The queue is
  • Complexity of task execution
  • Task priority

(2) a multithreaded

②.1 Principle of multithreading

  • forA single core CPUAt the same time,CPUOnly one thread can be processed, that is, only one thread is working (executing)
  • iOSIn theThe nature of simultaneous execution of multiple threadsisCPUFast switching between multiple tasks due toCPUScheduling threads fast enough creates the effect of multi-threaded “simultaneous” execution. Where the switching interval isTime slice

②.2 Significance of multi-threading

advantages

  • Can improve the execution efficiency of the program
  • Appropriately improve resource utilization (CPU, memory)
  • The task on the thread is automatically destroyed after it completes execution

disadvantages

  • Starting threads takes up a certain amount of memory (by default, each thread takes up512KB, creating a thread takes about90 millisecondsThe creation time of
  • If a large number of threads are enabled, a large amount of memory space will be occupied and the performance of the program will be reduced
  • The more threads,CPUThe more overhead there is on the calling thread
  • The program design is more complex, such as communication between threads, multithreaded data sharing

②.3 Life cycle of multithreading

The life cycle of multithreading is mainly divided into five parts:New – Ready – Run – Block – Die, as shown in the figure below

  • new: mainly instantiates thread objects
  • ready: thread object callstartMethod to a thread objectJoins a pool of schedulable threads.Wait for the CPU to callThat callstartMethod, which is not executed immediately, is enteredThe ready state, need to wait a period of time, afterCPUAfter dispatch, which means fromThe thread enters the running state
  • run:CPUResponsible for scheduling the execution of threads in the schedulable thread pool. A thread’s state may switch back and forth between ready and run before its execution is complete. State changes between ready and run are the responsibility of the CPU, not the programmer.
  • blocking: Yes when a predetermined condition is metUse sleep or lockBlock thread execution.sleepForTimeInterval(Specified sleep duration),sleepUntilDate(Sleep until the specified date),@synchronized(self)(Mutex)
  • death: There are two cases: normal death, that is, the thread has finished executing. Abnormal death, which is the termination of execution inside the thread (or in the main thread) after a condition is met (exit by calling the exit method, etc.)

A running thread has a period of time (called a time slice) in which it can execute.

  • ifTime slice exhausted, the thread will enterReady state queue
  • ifThe time slot is not used upAnd you need to startWait for something, will enterBlocking status queue
  • After an event occurs, the thread will re-enterReady state queue
  • Whenever aThread out of run, that is, after the execution is complete or forced to exit, the system will retryFrom the ready state queueIn theSelect a thread to continue execution

Exit and cancel instructions for the thread

  • exit: Once the thread is forcibly terminated, all subsequent code is not executed
  • cancel: Cancels the current thread, but not the executing thread

Does the higher priority of the thread mean the faster the task is executed?No, the speed of thread execution, in addition to the priority, also need to checkResource size(the complexity of the task),And CPU schedulingIn case.NSThread, thread prioritythreadPriorityHas been the quality of servicequalityOfServiceInstead, here are the related enumeration values

②.4 Principle of thread pool

  • [Step 1] Determine whether the core thread pool is all executing tasks

    • Return NO and create a new worker thread to execute
    • Return YES to enter [Step 2]
  • [Step 2] Determine whether the thread pool work queue is full

    • Returns NO and stores the task to a work queue waiting for CPU scheduling
    • Return YES to enter [Step 3]
  • 【 Step 3 】 Check whether all threads in the thread pool are in the executing state

    • Returns NO to schedule free threads from the schedulable thread pool to execute the task
    • Return YES to enter [Step 4]
  • [Step 4] Give saturation strategies to execute, mainly including the following four strategies (the following four strategies are not found in iOS)

    • AbortPolicy: Direct throwRejectedExecutionExeceptionException to prevent the normal operation of the system
    • CallerRunsPolicy: rolls back the task to the caller
    • DisOldestPolicy: Drop the most waiting task
    • DisCardPolicy: Directly discards the task

②.5 Implementation scheme of multi-threading in iOS

There are four main ways to achieve multithreading in iOS:Pthread, NSThread, GCD, and NSOperation, the summary is shown in the figureHere are simple examples of the above four scenarios

Bridge between C and OC The bridge between C and OC is described as follows

  • __bridgeI only do type conversions, butDo not modify object (memory) management rights
  • __bridge_retained(Also availableCFBridgingRetain) will beObjective-CObject is converted toCore FoundationObject, while putting the object (memory)The management is in our handsTo be used laterCFReleaseOr related methodsRelease object
  • __bridge_transfer(Also availableCFBridgingRelease) will beCore FoundationObject is converted toObjective-CObject, while willObject (memory) management is given to ARC

②.6 Thread safety

When multiple threads access a resource at the same time, data corruption and data security problems may occur. The following two solutions are available

  • Mutex (i.e. synchronous lock) :@synchronized
  • spinlocks
②.6.1 Mutex vs. spin lock

The mutex

  • Ensure that the code in the lock can be executed by only one thread at a time!
  • The lock range of mutex should be as small as possible. The larger the lock range, the worse the efficiency!
  • Mutex code, when a new thread access, if it is found that another thread is executing the locked code, the new thread will go to sleep
  • Any that can be lockedNSObjectobject
  • Note: The lock object must be accessible to all threads
  • If only one place in the code needs to be locked, use most of themselfTo avoid creating a separate lock object

spinlocks

  • A spin lock is similar to a mutex, but instead of blocking a thread by sleeping, it stays in place until the lock is acquiredBusy etc.(spinning in place, called spin) blocked
  • Usage scenario: The spin lock, property modifier is used when the lock is held for a short time and the thread does not want to spend too much on reschedulingatomicThere is one of themspinlocks
  • A spin lock was added so that when a new thread accesses code, it can use it if it finds that another thread is locking codeInfinite loopMethod that has been waiting for the locked code execution to complete, i.eConstantly trying to execute code, comparing performance

What is the difference between a spinlock and a mutex?

  • Similarity: Ensure that only one thread executes tasks at the same time, that is, ensure the corresponding synchronization function

  • Difference:

    • The mutex: Found another thread executing, current threaddormancy(i.e.The ready state), enter the wait execution, that is, suspend. Wait for another thread to open, then wake up execution
    • spinlocks: Found another thread executing, current threadBusy etc.(that is, access all the time), is in the busy state, which consumes high performance
  • Application scenario: Use different locks based on the task complexity

    • Current task status comparisonshortWhen usingspinlocks
    • On the contrary, useThe mutex
②.6.2 Differences between atomic and nonatomic

Atomic and nonatomic are mainly used for attribute modification, and the following are some related descriptions

  • nonatomicNonatomic property
  • atomicAtomic property (thread safety), designed for multithreading, default
    • Ensure that only one thread can write at a time (but multiple threads can write at a time)
    • atomicIt has a lock of its own (spinlocks)
    • Single write Multiple read: A single thread writes data and multiple threads can read data
  • atomic: Thread safe, consuming a lot of resources
  • nonatomic: Non-thread-safe, suitable for mobile devices with small memory

IOS development tips

  • All properties are declared asnonatomic
  • Avoid multiple threads grabbing the same resource. Try to transfer the service logic of locking and resource grabbing to the server to reduce the pressure on the mobile client

②.7 Communication between threads

inThreading Programming GuideIt is mentioned in the documentation that threads communicate in the following ways

  • Direct messagingThrough:performSelectorA set of methods for executing tasks specified by one thread on another. Since the execution context of the task is the target thread, messages sent in this manner will be automatically serialized
  • Global variables, shared memory blocks, and objectsAnother simple way to pass information between two threads is to use global variables, shared objects, or shared chunks of memory. Although shared variables are fast and easy, they are more fragile than direct messaging. Shared variables must be carefully protected using locks or other synchronization mechanisms to ensure correct code. Failure to do so may result in competitive conditions, data corruption, or crashes.
  • Conditional execution: Condition is a kind ofSynchronization toolCan be used to control when a thread executes a particular part of the code. You can treat a condition as a lock and let the thread run only when the specified condition is met.
  • Runloop sources: a customRunloop sourceConfiguration allows specific application messages to be received on a thread. Due to theRunloop sourceIs event-driven and therefore inWhen there is nothing else to do, the thread automatically goes to sleep, thus improving the efficiency of the thread
  • Ports and socketsBased on:Port communicationIs a more sophisticated way to communicate between two threads, but it is also a very reliable technique. More importantly, ports and sockets can be used to communicate with external entities, such as other processes and services. To improve efficiency, useRunloop sourceSo when there is no data waiting on the port, the thread will go to sleep. Note that port communication is requiredAdd ports to the main thread RunloopOtherwise it will not go to the port callback method
  • The message queueThe traditional multiprocessing service is definedFirst in, first out (FIFO)Queue abstraction for managing incoming and outgoing data. Although message queues are simple and convenient, they are not as efficient as some other communication technologies
  • Cocoa Distributed Objects: Distributed object is a kind ofCocoaTechnology that provides advanced implementations for port-based communication. Although it is possible to use this technique for interthread communication, it is strongly recommended not to do so because of the overhead involved. Distributed objects are better suited for communicating with other processes, although transactions between these processes are also expensive.

②.8 Comparison between GCD and NSOperation

  • The relationship between GCD and NSOperation is as follows:

    • GCDIt’s bottom-facingCThe language of theAPI
    • NSOperationIs to useGCDEncapsulating the build, yesGCDHigh level abstractions of
  • The comparison between GCD and NSOperation is as follows:

    • GCDExecution is more efficient, and because the queue is executed byblockThis is a lightweight data structure — much easier to write
    • GCDOnly supportFIFOWhileNSOprationYou can set the maximum number of concurrent requests, set priorities, and add dependencies to adjust the execution sequence
    • NSOprationYou can even set dependencies across queues, butGCDThis can only be done by setting up a serial queue, or by adding it to a queuebarrierTasks can control the execution sequence, which is more complex
    • NSOperationsupportKVO(Object oriented) can be detectedoperationWhether the execution is in progress, whether the execution is finished, and whether the execution is canceled

Second, the NSthread

NSthread is an official object-oriented thread operation technology provided by Apple. It is the upper layer encapsulation of threads and tends to be the bottom layer. Simple and convenient, you can directly operate thread objects, less frequent use.

① Creating a thread

There are three main ways to create a thread

  • throughinitInitialization mode
  • throughdetachNewThreadSelectorConstructor mode
  • throughperformSelector...Method creation, primarily for fetchingThe main thread, and background threads

(2) property

(3) class method

Common class methods are the following

  • currentThread: Gets the current thread
  • sleep...: blocking thread
  • exit: Exit thread
  • mainThread: Gets the main thread

Third, the GCD

(1) the GCD introduction

What is the communist party? Grand Central Dispatch, GCD, is pure C and offers a lot of powerful functions

Advantages of THE COMMUNIST Party of China

  • GCDIt’s appleMulti-core parallel computingProposed solutions
  • GCDAutomatically usingMore and moreThe CPU kernel(such as dual-core, quad-core)
  • GCDAutomatic managementThread life cycle (create thread, schedule task, destroy thread)
  • The programmer just needs to tellGCDWhat do you want to do without writing any thread management code

To sum up GCD in one sentence: Add a task to a queue and specify the function that executes the task

(2) the GCD core

In daily development,GCDIt is generally written in the following form

Break up the above code to make it easier to understandGCDThe core is mainly composed ofTask + queue + functionConstitute a

  • usedispatch_block_tCreate a task
  • usedispatch_queue_tCreate a queue
  • Adds the task to the queue and specifies the function that executes the taskdispatch_async

Note that tasks refer to operations. When creating tasks using dispatch_block_t, there are two main points

  • Task usingblockencapsulation
  • The task ofblockThere are no arguments and no return values

③ Functions and queues

(3). 1 function

There are two ways to execute tasks in the GCD, synchronous execution and asynchronous execution, respectively corresponding to the synchronous function dispatch_sync and asynchronous function dispatch_async

  • Synchronous executionCorresponding to the synchronization functiondispatch_sync
    • You must wait for the current statement to complete before executing the next statement
    • Thread will not be openedThat is, it does not have the ability to start new threads
    • Execute in the current threadblocktask
  • Asynchronous execution, corresponding to asynchronous functionsdispatch_async
    • The next statement can be executed without waiting for the current statement to complete
    • Will open the threadperformblockA task that has the ability to start a new thread (but not necessarily, depending on the queue type specified by the task)
    • Asynchrony is a byword for multithreading

To sum up, there are two main differences between the two modes of execution:

  • Whether to waitThe task in the queue is complete
  • Whether to start a new threadThe ability to

(3). 2 queue

In multithreadingThe queue(Dispatch Queue) refers toWait queues for executing tasks, the queue used to store tasks. Queues are a special kind of thingThe linear table, follow theFirst in, first out (FIFO)The principle is that new tasks are always inserted at the end of the queue, and reads of tasks start at the head of the queue. For each task read, a task is released from the active queue, as shown in the figure below

③ 2.1 Serial queue and Concurrent queue

inGCD, the queue is mainly divided intoSerial Dispatch QueueConcurrent Dispatch QueueTwo, as shown in the figure below

  • Serial queue: Only one task is executed at a time, waiting for the completion of the previous task before executing the next one, that is, only one thread is started (common sense: only one task is scheduled to execute at a time)

    • usedispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);Creating a serial queue
    • One of theDISPATCH_QUEUE_SERIALYou can also useNULLTheta is theta, both of these are thetaThe default serial queue
  • Concurrent queue: Multiple tasks can be executed concurrently, that is, multiple threads can be started and tasks can be executed simultaneously (common sense: multiple tasks can be scheduled at the same time)

    • usedispatch_queue_create("xxx", DISPATCH_QUEUE_CONCURRENT);Creating concurrent queues
    • Note: The concurrency function of concurrent queues is only available inAn asynchronous functionThe only effective
③ 2.2 Primary queue and global Concurrent queue

The GCD provides a Main Dispatch Queue and a Global Dispatch Queue for the above two types of queues.

  • Main Dispatch Queue: A special serial Queue provided in the GCD

    • Dedicated to theA serial queue that schedules tasks on the main thread, depends on theThe main thread,The main RunloopIn theThe main functionBefore you callAutomatically created
    • Thread will not be opened
    • If the main thread is currently running tasks, then whatever tasks are currently added to the main queue will not be scheduled
    • usedispatch_get_main_queue()Get the main queue
    • Usually in returnThe main thread updates the UIThe use of
  • Global Dispatch Queue: The default concurrency Queue provided by the GCD

  • For programmers’ convenience, Apple provides global queues

  • When using multithreaded development, if there is no special need for queues, you can use global queues directly when performing asynchronous tasks

  • The simplest is dispatch_get_global_queue(0, 0).

    • The first parameter saysQueue priority, the default priority isDISPATCH_QUEUE_PRIORITY_DEFAULT=0In theios9After that, quality has been served(quality - of - service)replace
    • The second argument uses 0
③ 2.3 Global concurrent queue and primary queue are used together

In daily development,Global queue + concurrent juxtapositionThis is generally used together

③.3 Different combinations of functions and queues

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

③.3.1 Serial queue + synchronization function

Tasks are executed one after another in the current thread,No new thread is created

③ 3.2 Serial queue + asynchronous function

One task after another is executed,A new thread will be created

③.3.3 Concurrent queue + synchronization function

Tasks are executed one after another without opening up a thread

③.3.4 Concurrent queue + Asynchronous function

taskout-of-orderTo perform,A new thread will be created

③ 3.5 Main queue + synchronization function

taskWait for each other.Cause a deadlock Deadlock causes are analyzed as follows:

  • The main queue has two tasks, in the order:CJNSLog taskThe synchronization block
  • performCJNSLogAfter the task is complete, synchronization is performedBlock, will add task 1 (when I =1) to the main queue. The main queue sequence is as follows:CJNSLog task - Synchronization block - Task 1
  • Task 1Execution needs ofWait for the synchronization block to completeBefore they are executed, andSynchronize the execution of blocksNeed to beWait until task 1 is completeAnd so it wasTasks waiting for each otherIs caused byDeadlock collapse

Deadlock phenomenon

  • The main threadBecause of youSynchronization functionIs waiting to be executed first
  • The home side columnWait for the main thread to complete the task before executing your own task
  • Main queue and main threadWaiting for each other causes a deadlock
③ 3.6 Main queue + asynchronous function

One task after another is executed,Do not open a thread

③.3.7 Global concurrent queue + synchronization function

One task after another is executed,No new thread is opened

③ 3.8 Global Concurrent Queue + Asynchronous function

taskout-of-orderTo perform,A new thread will be created

(3). 3.9 summary
Functions and queues Serial queues Concurrent queue The home side column Global concurrent queue
Synchronization function Execute sequentially without opening up threads Execute sequentially without opening up threads A deadlock Execute sequentially without opening up threads
An asynchronous function Sequential execution opens up threads Out of order execution, open up threads Execute sequentially without opening up threads Out of order execution, open up threads

(4) dispatch_after

(5) dispatch_once

6 dispatch_apply

All landowners dispatch_group_t

Dispatch_group_t: scheduling group task execution group, can listening task group, and set up the wait time application scenarios: multiple interfaces request after refresh the page has the following two types of use

⑦.1 Use dispatch_group_async + dispatch_group_notify

dispatch_group_notifyindispatch_group_asyncNotification will be received when the execution is complete

⑦.2 Use dispatch_group_Enter, dispatch_group_leave, and dispatch_group_notify

dispatch_group_enteranddispatch_group_leavePairs appear, so that the logic of entering and leaving the group is clearer

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_wait based on ⑦.2

Today dispatch_barrier_sync & dispatch_barrier_async

Fence functions are used in two main scenarios: serial queue and concurrent queue. Application Scenarios:Synchronization lock

After the tasks added to the queue are complete, the tasks added to the queue are added to the queue. 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

Do not use the fence function

Using the fence functionThe purpose of the fence function is to group tasks in a queue, so we just have to pay attentionTask 1,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

Do not use the fence function

Using the fence function

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

Today. 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

In case twodispatch_barrier_asynctodispatch_barrier_sync

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 queue: In everyday use, for exampleAFNetworkingWhy can’t we use the fence function to block the synchronization lock when making network requests, becauseAFNetworkingIt has its own queue internally

Pet-name ruby dispatch_semaphore_t

Semaphores are primarily used as synchronization locks to control the maximum number of concurrent GCDS

  • dispatch_semaphore_create(): Creates a semaphore
  • dispatch_semaphore_wait(): Waiting for semaphore, semaphoreMinus 1.when the 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(): Release semaphore, semaphoreAdd 1.when the semaphore> = 0Will performwaitThe code after that.

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

Using semaphoresAPITo rewrite the code

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

  • i=0It may be printed first, or it may be sent firstwaitSemaphore -1, butwaitThen a semaphore of zero does not block the thread, so enteri=1
  • i=1It may be printed first, or it may be sent firstwaitSemaphore -1, butwaitThen a semaphore of -1 blocks the thread and waitssignalCarry on

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

Attending dispatch_source

Dispatch_source_t is primarily used for timing operations because it creates timers that do not rely on RunLoop and are more accurate than NSTimer

⑩ 1 Definition and Usage

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 files orsocketEvent source when a file orsocketNotification when data changes
  • Process Dispatch Source: Listens for process event sources, notifications of process-related events
  • Mach port Dispatch Source: listeningMachPort Event Source
  • 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 timers

Generally used in iOS developmentNSTimerTo handle timing logic, butNSTimerIs dependent onRunloop, andRunloopCan run in different modes. ifNSTimerAdd in a mode whenRunloopWhen running in other modes, the timer hangs up; And ifRunloopIn the blocked state,NSTimerThe trigger time will be delayed until the next oneRunloopWeeks.. soNSTimerThere are errors in timing, it’s not particularly accurate, andThe GCD timerDo not rely onRunloopThe timing accuracy is much higher

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_cancelcanceltimer
  • dispatch_resumeanddispatch_suspendThe number of calls needs to be balanced
  • sourceinPending stateIf you want to directly setsource = nilOr recreate itsourceCan causecrashThe correct way is to call it while activedispatch_source_cancel(source)Release currentsource

Fourth, the NSOperation

NSOperation is an abstract class and depends on subclasses NSInvocationOperation and NSBlockOperation to implement it

The following is a comparison of the developer documentationNSOperationA paragraph describing

1) NSInvocationOperation

  • The basic use

  • Transactions are processed directly without adding hidden queues

  • This leads to the following mis-use code

The code above crashes because of the thread lifecycle:

  • queue addOperation:opAction tasks that process transactions have been queued and let the thread run
  • op startRunning an already running thread again causes thread clutter

(2) NSBlockOperation

The difference between NSInvocationOperation and NSBlockOperation is:

  • The former is similar totargetIn the form of
  • The latter is similar toblockFormal – Functional programming, more readable business logic code

NSOperationQueue is executed asynchronously, so the order of completion of tasks one and two is uncertain

throughaddExecutionBlockThis is a way to makeNSBlockOperationImplementing multithreading

(3) User-defined subclasses inherited from NSOperation to encapsulate tasks by implementing internal corresponding methods

(4) NSOperationQueue

NSOperationQueue has two types of queues: primary queue and other queues. Other queues contain both serial and concurrent queues.

  • Main queue: Tasks on the main queue are executed on the main thread
  • Other queues (not the main queue) : Join toThe master queueIn the default task is concurrent, enable multithreading

For example, we said in ② NSBlockOperation.

⑤ Execution sequence

The following code demonstrates the effect of the operation and queue executionAsynchronous concurrentthe

⑥ Set the priority

Setting the priority of NSOperation will only make the CPU more likely to call it

  • Do not usesleep— High priorityTask aPrior to low priorityTask 2
  • usesleepDelay — high priorityTask aSlower than lower priorityTask 2

⑦ Set the number of concurrent requests

  • inGCDYou can only use semaphores to set the number of concurrent requests
  • whileNSOperationYou can easily set the number of concurrent requests
    • By setting themaxConcurrentOperationCountTo control the number of tasks to be executed in a single queue

Add a dependency

inNSOperationAdding dependencies to the command can control the task execution sequence

⑨ Communication between threads

  • inGCDTo make network requests asynchronously, then go back to the main thread to refreshUI
  • NSOperationThere are also operations similar to communication between threads

⑩ Suspend, continue, or cancel tasks

However, it is common to encounter some strange problems in the use of the task is suspended, but still continue to execute several tasks before stopping

Here is the case with concurrency of 2:

  • Hang up before:Task 3,Task 4Waiting to be dispatched
  • Suspension moment:Task 3,Task 4They cannot be suspended when they have been scheduled out of the queue for execution
  • After the pending:Task 3,Task 4Is executed by a thread while the original queue is suspended and cannot be scheduled

Fifth, GCD bottom analysis

Because of the large length of the source code, logical branches, macro definitions, so that the source code becomes difficult to understand, so that developers are discouraged. But if you look at the source code with questions and purpose, you can reduce the difficulty and ignore irrelevant code. First of all, we put forward several problems to be analyzed:

  • The queue to create
  • An asynchronous function
  • Synchronization function
  • The singleton principle
  • Principle of fence function
  • Principle of semaphore
  • The principle of scheduling groups

The source of the source code

Analysis of the source code first to obtain GCD source, before has analyzed objc, malloc, dyLD source, so GCD content is in which source?

Here’s a tip. Given that you know you’re going to study GCD, there are several ways to select source code

  • Baidu/Google
  • Lower sign breakpointdispatch_queue_createordispatch_asyncOpen assembly modeDebug->Debug Workflow->Always show Disassembly

That’s where the libdispatch-1271.40.12 source code comes in

② Queue creation

We know from previous studies that the creation of queues is done byGCDIn thedispatch_queue_createMethod, so it can be searched in the source codedispatch_queue_create.Suppose we just searchdispatch_queue_createIf so, there will be many situations (66 results in 18 files), this time to test a developer to read the source code experience

Here, we’ll change the search criteria:

  • Since the queue creation code isdispatch_queue_create("XXX", NULL), so searchdispatch_queue_create(— Drop the filter result to (21 results in 6 files)

  • Since the first argument is a string, inThe c languageusingconstModify, so searchdispatch_queue_create(constDrop filter result to (2 results in 2 files)

(2). 1 dispatch_queue_create

Regular mid-tier encapsulation — easy to iterate code without changing upper-layer usage

Sometimes we need to pay attention to the parameter passing in the source function:

  • At this timelabelIs the upperReverse the full domain name, mainly used for crash debugging
  • attrisNULL/DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_CONCURRENTIs used to distinguish between asynchronous and synchronous queues

#define DISPATCH_QUEUE_SERIAL NULL The serial queue macro definition is NULL

(2). 2 _dispatch_lane_create_with_target

  • 1. By_dispatch_queue_attr_to_infoMethods the incomingdqa(That is, the queue type,Serial, concurrentEtc.) to createdispatch_queue_attr_info_tObjects of typedqaiforAttributes of the storage queue

    • dispatch_queue_attr_info_twithisaIs a bit-domain structure used to store information about attributes of queues
  • 2. Set attributes associated with queues, such as qos

  • 3. Use DISPATCH_VTABLE to join queue names, that is, vtable. DISPATCH_VTABLE is a macro definition, as shown in the following

    • Serial queue type:OS_dispatch_queue_serial, verify as follows

    • Type of concurrent queue:OS_dispatch_queue_concurrent, verify as follows

  • Dqai. dqai_concurrent is the Boolean value of the _dispatch_queue_init parameter. Vtable indicates the type of the queue, indicating that the queue is also an object

    • Enter the_dispatch_object_alloc -> _os_object_alloc_realizedMethod is setisaWe can verify from hereQueues are also objectsthe

    • Enter the_dispatch_queue_initMethod, the queue type isdispatch_queue_tAnd sets the queue properties

  • Through 5._dispatch_trace_queue_createProcess the queue created, where_dispatch_trace_queue_createis_dispatch_introspection_queue_createThe encapsulated macro definition will eventually return the processed one_dq

    • Enter the_dispatch_introspection_queue_create_hook -> dispatch_introspection_queue_get_info -> _dispatch_introspection_lane_get_infoAs you can see, there are some differences with our custom class,Create a queueThe underlying implementation isCreating a Vm using a Templatethe

(2). 3

  • Queue creation methoddispatch_queue_createParameter two, the queue type, determines the underlying mediummax & 1(used to distinguish between serial and concurrent), where1 indicates serial
  • queueIt’s also an object that needs to be passed by the underlying layeralloc + initCreate, and inallocThere is one of themclasstheclassIs concatenated through macro definitions and is specified at the same timeisaThe point to
  • Create a queueThe underlying processing is created from a template of typedispatch_introspection_queue_sThe structure of the body

dispatch_queue_createThe underlying analysis process is shown in the following figure

③ Asynchronous function

(3). 1 dispatch_async

Two functions are analyzed

  • _dispatch_continuation_init: task wrapper function
  • _dispatch_continuation_async: concurrent processing function

2 _dispatch_continuation_init Task wrapper

Basically wrap the task and set the thread’s backhaul function, equivalent toInitialize the

There are mainly the following steps

  • Copy tasks using _dispatch_Block_copy

  • through_dispatch_Block_invokeEncapsulate tasks, where_dispatch_Block_invokeIs a macro definition, according to the above analysisAn asynchronous callback

  • If it is synchronous, the callback function is assigned the value _dispatch_call_block_and_release

  • through_dispatch_continuation_init_fMethod assigns a value to the callback function, i.efisfuncSave it in a property

3 _dispatch_continuation_async Concurrent processing

In this function, the main thing isPerform the block callback

  • The key code isdx_push(dqu._dq, dc, qos).dx_pushIs the macro definition, as shown below

  • And one of thedq_pushDepending on the type of queue, different functions need to be executed

Here we debug the execution function through symbolic breakpoints

  • rundemoTo determine which function is being executed by a symbolic breakpoint, since it is a concurrent queue, by increasing_dispatch_lane_concurrent_pushSign breakpoint to see if it goes here

  • The run found that it did go_dispatch_lane_concurrent_push

  • Enter the_dispatch_lane_concurrent_pushSource code, found in two steps, continues through the symbol breakpoint_dispatch_continuation_redirect_pushand_dispatch_lane_pushDebug, and find that go is_dispatch_continuation_redirect_push

  • Enter the_dispatch_continuation_redirect_pushSource code, found to go againdx_pushA queue is also an object with a parent class and a root class, so it recurses to the root class

  • Next, through the root class_dispatch_root_queue_pushSign breakpoint, to verify whether the guess is correct, from the run result, it is completely correct

  • Go to _dispatch_root_queue_push -> _dispatch_root_queue_PUSH_inline -> _dispatch_root_queue_POKE -> The _dispatch_root_queue_poke_slow source code, verified by symbolic breakpoints, does go here, view the source code implementation of this method, there are two main steps

    • through_dispatch_root_queues_initMethod to register a callback
    • throughdo-whileLoop to create thread, usingpthread_createmethods

(3). 4 _dispatch_root_queues_init

  • Enter the_dispatch_root_queues_initSource code implementation, discovery is adispatch_once_fSingleton (see the underlying analysis of subsequent singleton, not described here), which is passed infuncis_dispatch_root_queues_init_once

  • Enter the_dispatch_root_queues_init_onceThe source code, its internal different transaction call handles are_dispatch_worker_thread2

Its block callback executes the call path: _dispatch_root_queues_init_once ->_dispatch_worker_thread2 -> _dispatch_root_queue_drain -> _dispatch_root_queue_drain -> _dispatch_continuation_pop_inline -> _dispatch_continuation_invoke_inline -> _dispatch_client_callout -> dispatch_call_block_and_release

This path can go through the breakpoint,btPrint stack information out

One thing to note here is that there is a difference between a singleton block callback and an asynchronous function’s block callback

  • A single case,blockIn the callbackfuncis_dispatch_Block_invoke(block)
  • And in an asynchronous function,blockIn the callbackfuncisdispatch_call_block_and_release

(4) summarize

To sum up, the underlying analysis of asynchronous functions is as follows

  • The preparatory work: First, copy and encapsulate the asynchronous task and set the callback functionfunc
  • Block the callback: Bottom passdx_pushRecursively, it redirects to the root queue and then passespthread_creatThe thread is created and finally passeddx_invokeperformblockCallbacks (notedx_pushanddx_invokeIt’s in pairs.)

The underlying analysis flow of asynchronous functions is shown in figure 1

④ Synchronization function

(4). 1 dispatch_sync

The underlying implementation isBy barrier function(See below for a low-level analysis of the fence function)

_dispatch_sync_f (4). 2

(4). 3 _dispatch_sync_f_inline

Look at the _dispatch_sync_F_inline source code, where width = 1 indicates a serial queue, and there are two important points:

  • The fence:_dispatch_barrier_sync_f(It can be explained by the underlying analysis of the fence function in the following article)Synchronization functionThe underlying implementation of theSynchronous fence function
  • Deadlock:_dispatch_sync_f_slowIf there is a mutual wait, a deadlock will occur

(4). 4 _dispatch_sync_f_slow deadlocks

Enter the_dispatch_sync_f_slowThe currentThe home side columnisSuspend, blockthe

  • Adding a task to a queue willpushJoin the main queue, enter_dispatch_trace_item_push

  • Enter the__DISPATCH_WAIT_FOR_QUEUE__To determinedqWhether it is a waiting queue and then gives a statusstateAnd thendqMatches the status of the queue on which the current task depends

  • Enter the_dq_state_drain_locked_by -> _dispatch_lock_is_locked_byThe source code

If the current queue is the same as the queue that is executing, that is, check whether the thread ID is equal. If the thread ID is equal, the deadlock will be caused

_dispatch_sync_invoke_and_complete -> _dispatch_sync_function_invoke_inline source code has three main steps:

  • Push tasks into queues: _dispatch_thread_frame_push
  • On dutyblockThe callback:_dispatch_client_callout
  • Out of the team:_dispatch_thread_frame_pop

As you can see from the implementation, the tasks are pushed into the queue first, then the block callback is performed, and then the tasks are popped, so the tasks are executed sequentially

(4). 5

The underlying implementation of the synchronization function is as follows:

  • Synchronization functionThe underlying implementation of theSynchronous fence function
  • If in the synchronization functionThe currently executing queue is the same queue as the waiting queueTo formWait for each other“, will cause deadlock

To sum up, the underlying implementation process of the synchronization function is shown in the figure

(5) dispatch_once

In daily development, we generally useGCDthedispatch_onceTo create a singleton, as shown below

First, there are two things we need to know about singletons

  • The reason for executing once: Singleton processes are executed only once, how is the underlying control, i.e. why can only be executed once?
  • Block call timing: the singletonblockWhen was the call made?

With the above two questions, we will analyze the bottom layer of singleton

5. 1 dispatch_once

Enter dispatch_once source code implementation, the bottom is through dispatch_onCE_F implementation

  • Parameter 1:onceToken, it is a static variable. Since static variables are defined differently in different locations, static variables are unique
  • Argument 2:blockThe callback

Dispatch_once_f (5). 2

Enter the source code of dispatch_once_F, where val is the onceToken static variable passed in from the outside world, and func is _dispatch_Block_invoke(block), where the underlying singleton is mainly divided into the following steps

  • willval, that is,A static variableconvertdispatch_once_gate_tThe type ofThe variable l
  • throughos_atomic_loadOf the current taskIdentifier v
    • ifvIs equal to theDLOCK_ONCE_DONE, indicates that the task has been executedreturn
    • If after the mission is executed,Locking failure, then walk to_dispatch_once_mark_done_if_quiescedFunction to store again, setting the identifier toDLOCK_ONCE_DONE
    • Otherwise, it passes_dispatch_once_gate_tryenterTry to enter the task, which is unlocked, and then execute_dispatch_once_calloutperformblockThe callback
  • If a task is being executed and another task 2 is entered, it passes_dispatch_once_waitThe function lets task 2 enterInfinite wait

5. 3 _dispatch_once_gate_tryenter unlocked

View the source code, mainly through the underlying layeros_atomic_cmpxchgMethod. If there is no problem in the comparison, lock is performed, that is, the identifier of the task is set toDLOCK_ONCE_UNLOCKED

5. 4 _dispatch_once_callout callback

Enter the _dispatch_once_callout source code, there are two main steps

  • _dispatch_client_callout:blockThe callback execution
  • _dispatch_once_gate_broadcast: Broadcast

  • Enter the_dispatch_client_calloutSource code, the main is implementationblockCallback, wherefIs equal to the_dispatch_Block_invoke(block), asynchronous callback

  • Enter the_dispatch_once_gate_broadcast -> _dispatch_once_mark_doneSource code, is mainly todgo->dgo_onceA value, and then set the task identifier toDLOCK_ONCE_DONE, i.e.,unlock

5. 5

The basic implementation of singletons is described as follows:

  • The singleton is executed only once: In the GCD singleton, there are two important parameters, onceToken and block. OnceToken is a static variable with uniqueness and is encapsulated into variable L of type dispatch_once_gate_t at the bottom layer. L is mainly used to obtain the association of encapsulation of the underlying atom, namely variable V. Run v to query the task status. If v is equal to DLOCK_ONCE_DONE, the task has been processed once. Return

  • Block call timing: if the task has not been executed at this time, the task will be locked through C++ function comparison, that is, the task state is set to DLOCK_ONCE_UNLOCK, to ensure the uniqueness of the current task execution and prevent multiple definitions elsewhere. After the block callback is executed, the current task is unlocked and the status of the current task is set to DLOCK_ONCE_DONE. The next entry will not be executed and will directly return

  • Impact of multi-threading: If other tasks come in during the execution of the current task, it will enter infinite wait times. The reason is that the current task has obtained the lock and locked, and other tasks cannot obtain the lock

The underlying process analysis of a singleton is as follows

⑥ Fence function

There are two main types of fence functions used in GCD

  • synchronousBarrier functiondispatch_barrier_sync(executed in the main thread) : The previous task will not come here until it completes, but the synchronous fence function willPlug thread, affecting the execution of subsequent tasks
  • asynchronousBarrier functiondispatch_barrier_async: Will come here after the previous mission is completed

The most direct function of the fence function is to control the task execution order, so that the synchronous execution

The fence function requires attention to the following points

  • Fence functions can only be controlledSame concurrent queue
  • Synchronous fenceWhen I add to the queue,The current thread will be lockedUntil the task before the synchronization fence and the synchronization fence task itself have finished, the current thread will open and continue to execute the next line of code
  • When using the fence function, use theCustom queueTo be meaningful
    • If the fence function is usedGlobal queue.Run crashThe reason is that the system is also using a global concurrent queue, using a fence will block the system at the same time, so it will crash
    • If the custom concurrent queue is changed to serial queue, that is, serial queue itself is ordered synchronization, then fencing will waste performance

⑥.1 Asynchronous fence function

Enter thedispatch_barrier_asyncSource code implementation, its underlying implementation anddispatch_asyncSimilarly, I won’t do the analysis here, but you can explore it yourself if you are interested

⑥.2 Synchronization fence function

6. 2.1 dispatch_barrier_sync

Enter thedispatch_barrier_syncSource code, implementation as follows

6. 2.2 _dispatch_barrier_sync_f_inline

Enter the_dispatch_barrier_sync_f -> _dispatch_barrier_sync_f_inlineThe source codeIt is mainly divided into the following parts

  • Get the thread ID by _dispatch_tid_self

  • through_dispatch_queue_try_acquire_barrier_syncDetermining thread status

    • Enter the_dispatch_queue_try_acquire_barrier_sync_and_suspendRelease here
  • Recursively finds the target of the fence function by _dispatch_SYNc_RECURse

  • through_dispatch_introspection_sync_beginProcess forward information

  • through_dispatch_lane_barrier_sync_invoke_and_completeperformblockAnd release the

All landowners semaphore

The function of semaphores is generally toSynchronize tasks, similar to theThe mutex, the user can control as neededGCDMaximum number of concurrent requests

Let’s analyze the underlying principles

7. 1 dispatch_semaphore_create created

The underlying implementation of this function is as follows, mainlyInitialize the semaphoreAnd set the maximum number of concurrent GCD’sThe maximum number of concurrent requests must be greater than 0

7. 2 dispatch_semaphore_wait lock

Os_atomic_dec2o specifies the — operation on the semaphore dsema. Inside, the C++ atomic_fetch_sub_explicit method is executed

  • ifvalue >= 0“Indicates that the operation is invalid and the operation succeeds
  • ifvalue = LONG_MINAnd the system will throw onecrash
  • ifvalue < 0, then enter theLong waiting

The os_ATOMIC_DEC2O macro definition is translated as follows

os_atomic_inc2o(p, f, m) 
os_atomic_sub2o(p, f, 1, m)
_os_atomic_c11_op((p), (v), m, sub, -)
_os_atomic_c11_op((p), (v), m, add, +)
({ _os_atomic_basetypeof(p) _v = (v), _r = \
        atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), _v, \ memory_order_##m); (__typeof__(_r))(_r op _v); })
Copy the code

Substitute the specific value into zero

os_atomic_dec2o(dsema, dsema_value, acquire);
os_atomic_sub2o(dsema, dsema_value, 1, m)
os_atomic_sub(dsema->dsema_value, 1, m)
_os_atomic_c11_op(dsema->dsema_value, 1, m, sub, -)
_r = atomic_fetch_sub_explicit(dsema->dsema_value, 1, equivalent to dsema-> dsemA_value -1
Copy the code

Enter the_dispatch_semaphore_wait_slowSource code implementation, whenvalue < 0According to the waiting eventtimeoutDo different things

7. 3 dispatch_semaphore_signal unlocked

Os_atomic_inc2o specifies the “++” function for value. Os_atomic_inc2o specifies the “++” function for value

  • ifvalue > 0“Indicates that the operation is invalid and the operation succeeds
  • ifvalue < 0, then enter theLong waiting

The os_ATOMIC_DEC2O macro definition is translated as follows

os_atomic_inc2o(p, f, m) 
os_atomic_add2o(p, f, 1, m)
os_atomic_add(&(p)->f, (v), m)
_os_atomic_c11_op((p), (v), m, add, +)
({ _os_atomic_basetypeof(p) _v = (v), _r = \
        atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), _v, \ memory_order_##m); (__typeof__(_r))(_r op _v); })
Copy the code

Substitute the specific value into zero

os_atomic_inc2o(dsema, dsema_value, release);
os_atomic_add2o(dsema, dsema_value, 1, m) 
os_atomic_add(&(dsema)->dsema_value, (1), m)
_os_atomic_c11_op((dsema->dsema_value), (1), m, add, +)
_r = atomic_fetch_add_explicit(dsema->dsema_value, 1), equivalent to dsema-> dsemA_value +1
Copy the code

7. 4

  • dispatch_semaphore_createThe main is to initialize the number limit
  • dispatch_semaphore_waitIt’s on the semaphorevaluefor--, that is, lock operation
  • dispatch_semaphore_signalIt’s on the semaphorevaluefor++, that is, unlock

In summary, the underlying operation of semaphore correlation functions is shown in figure

⑧ Principle of scheduling group

The most immediate effect of scheduling groups isControl task execution sequence, the common methods are as follows

Dispatch_group_create today. 1

  • Enter thedispatch_group_createThe source code

Basically creatinggroupAnd set the properties at this timegrouptheA value of 0

  • Enter the_dispatch_group_create_with_countSource code, which is ongroupObject property, and returnsgroupObject, of whichN is equal to zero

Today. 2 dispatch_group_enter into groups

Enter thedispatch_group_enterSource code, viaos_atomic_sub_orig2orightdg->dg.bits--Operation to process values

Today. 3 dispatch_group_leave group

The dispatch_group_leave source code is displayed

  • Minus 1 to 0, which is++operation
  • According to the state,do-whileLoop, wake up executionblocktask
  • if0 plus 1 is 1.enter-leaveUnbalanced, i.eleaveIf I call it multiple times, it willcrash

  • Enter the_dispatch_group_wakeThe source code,do-whileLoop makes asynchronous hits, calls_dispatch_continuation_asyncperform

  • Enter the_dispatch_continuation_asyncThe source code

This step is consistent with the block callback execution of asynchronous functions and is not explained here

Dispatch_group_notify notice today. 4

Enter thedispatch_group_notifySource code, ifold_stateIs equal to the0, can be releasedIn addition toleaveCan be achieved by_dispatch_group_wakeWake up, wheredispatch_group_notifyIt can also be awakened

  • Among themos_mpsc_push_update_tailIs a macro definition used to getdgThe status code

Dispatch_group_async today. 5

Enter thedispatch_group_asyncSource code, mainlyThe packing taskandAsynchronous processingtask

  • Enter the_dispatch_continuation_group_asyncSource code, mainly encapsulateddispatch_group_enterInto a set of operations

  • Enter the_dispatch_continuation_asyncSource code, perform regular asynchronous function underlying operations. Given theenter, there must be someleave, we guessLeave is implicitly executed after block execution, through breakpoint debugging, printing stack information

  • search_dispatch_client_calloutIs called in_dispatch_continuation_with_group_invokeIn the

Therefore, it is perfectly verified that the bottom layer of dispatch_group_async encapsulates enter-leave

Today. 6

  • enter-leaveAs long as it’s in pairs, near or far
  • dispatch_group_enterAt the bottom is throughC++Function,groupthevaluefor--Operation (i.e.,0 -> -1)
  • dispatch_group_leaveAt the bottom is throughC++Function,groupthevaluefor++Operation (i.e.,1 - > 0)
  • dispatch_group_notifyAt the bottom is mostly judgmentgroupthestateWhether is equal to the0whenEqual to zero, notify
  • blockTask wake up can be passeddispatch_group_leave, can also passdispatch_group_notify
  • dispatch_group_asyncIs equivalent toenter - leave, and the underlying implementation isenter-leave

To sum up, the underlying analysis process of the scheduling group is shown in the figure below

Six, related questions analysis

① Asynchronous function + parallel queue

What is the output order of the following code?

The asynchronous function does not block the main queue, but opens a new thread to perform the asynchronous task

The following figure shows the analysis roadmap. The red line indicates the task execution sequence

  • The main threadThe task queue of is:Task 1, asynchronous block1, Task 5, includingAsynchronous block1It takes a lot of performance,Task 1andTask 5The task complexity is the same, soTasks 1 and 5 take precedence over asynchronous Block1 execution
  • inAsynchronous block1Where, the task queue is:Task 2, asynchronous block2, task 4, includingblock2Relatively expensive performance,Task 2andTask 4It’s the same complexity, soTasks 2 and 4 take precedence over Block2
  • The last executionblock2In theTask 3
  • In extreme cases, it can occurTask 2beforeTask 1andTask 5Execute because the current is presentThe main thread is stuckordelayIn the case

Extending a willParallel linestoSerial queuesIt doesn’t make any difference. The order is still 11, 5, 2, 4, 3

Expansion two sleeps for 2s before Mission 5, i.esleep(2), the execution sequence is:One, two, four, three, fiveThe reason is that I/O printing is simpler than sleeping for 2s, so asynchronousblock1Will precedeTask 5Of course, if the main queue is blocked, there will be another order of execution

② Asynchronous function nested synchronous function + concurrent queue

What is the output order of the following code?Analysis is as follows:

  • Task 1Task 5Is the same as the previous analysis, and the execution sequence isTask 1 Task 5 Asynchronous block
  • inAsynchronous blockIs executed firstTask 2And then walk toThe synchronization blockThe synchronization function blocks the main thread, soTask 4Need to wait forTask 3It can’t be executed until it’s done, soAsynchronous blockThe order of execution is:Task 2 Task 3 Task 4

③ Asynchronous function nested synchronous function + serial queue (i.e. synchronous queue)

What is the order in which the following code is executed? What happens? Why is that?

The analysis is shown in the following figure, where red indicates the task execution sequence and black dotted line indicates waiting

  • Executed firstTask 1, and thenAsynchronous block, does not block the main thread, and is more complex than task 5, so it takes precedenceTask 5, in theAsynchronous block
  • inAsynchronous blockRun the command firstTask 2, and thenThe synchronization block.The synchronization function blocks the thread, so executeWait until task 3 is complete for task 4And theTask 3The implementation of the needWait for the asynchronous block to complete, which is equivalent toTask 3 Wait for task 4complete
  • So that’s what happenedTask 4 Wait for task 3.Task 3 Wait for task 4That is, the situation of waiting for each otherA deadlockThe key point here is the key stackslow

Expand one removeTask 4What is the order of execution? Or will itA deadlockBecause theTask 3 waits for the asynchronous block to completeAnd theAsynchronous block waits for task 3.

④ Asynchronous function + synchronous function + concurrent queue

What is the order in which the following code is executed? A: 1230789 B: 1237890 C: 3120798 D: 2137890Analysis of the

  • Task 1Task 2Because it isAsynchronous function + concurrent queue, will open the thread, so there is no fixed order
  • Task 7,The task of 8,The task of 9Similarly, threads are opened, so there’s no fixed order
  • Task 3isSynchronization function + concurrent queue, the synchronization function blocks the main thread,But it will only block 0So, it is certain that0 must come after 3.Before 789,

The following is a print of the different execution orders

⑤ In the following code, how many types of queues are there?

There are two types of queues: concurrent queues and serial queues

  • Serial queue:serial,mainQueue
  • Concurrent queue:conque,globalQueue

Write in the back

Study harmoniously without being impatient. I’m still me, a different color of fireworks.