preface
“This is the 17th day of my participation in the August More Text Challenge.
Resources to prepare
- Multithreading apple official documentation
objc
Source code download:Multiple versions of objC source code
Threads and processes
Definitions of threads and processes
What is a process
- A process is an application that is running in the system;
- Each process is independent and runs in its own dedicated and protected memory space;
- It can be viewed through the activity monitor
Mac
The processes that are started in the system;MAC
It’s multi-process,iOS
It is single-process.
What is a thread
- Thread is the basic execution unit of a process. All tasks in a process are executed in the thread.
- In order for a process to perform a task, it must have at least one thread.
- By default, the program starts a thread called the main thread or UI thread.
The process contains multiple threads, the process is responsible for task scheduling, thread is responsible for task execution. Multiple processes are not supported in iOS. All applications run as a single process, independent of each other.
The relationship between threads and processes
The relationship between process and thread involves two aspects: address space and resource ownership:
Address space: Threads of the same process share the address space of the same process, while processes are independent of each other.
Resource ownership: Threads of the same process share resources of the same process, such as memory, I/O, and CPU. Resources of different processes are independent.
The use characteristics of both:
- (1) The crash of one process in protected mode has no impact on other processes, but the crash of one thread will result in the death of the entire process. So multi-process is more robust than multi-thread;
- (2) Process switching consumes large resources and high efficiency. So when it comes to frequent switching, threads are better than processes. If concurrent operations are required and some variables are shared, only threads can be used, not processes.
- (3) Execution process: each independent process has a program running entrance, sequential execution sequence and program entrance. However, threads cannot be executed independently and must depend on the application program, which provides multiple thread execution control.
- Thread is the basic unit of processor scheduling, but process is not;
- (5) Threads have no address space, threads are contained in the process address space;
Thread-local storage (TLS
)
Thread Local Storage: A Thread does not have an address space, but does have thread-local Storage. Thread-local storage is the private space that some operating systems provide for threads individually, but usually with very limited capacity.
This analysis was covered in the previous article on class loading. The _objc_init method contains the TLS initialization operation in objC source code, as shown below:
Implementation in TLs_init:
multithreading
Principle of multithreading
In iOS, multi-threading is switched quickly between multiple tasks by CPU, and the time of CPU scheduling thread is fast enough, resulting in the effect of simultaneous execution of multi-threading. So, multithreading is not really concurrency. True concurrency must be built on a multi-core CPU.
Meaning of multithreading
-
Advantages:
-
- Appropriately improve the efficiency of execution.
-
- Appropriate utilization of resources (
CPU
, memory, etc.)
- Appropriate utilization of resources (
-
- The thread is automatically destroyed after the task is completed.
-
-
disadvantages
-
- Starting threads takes up a certain amount of memory (see thread cost below). By default, each thread takes up
512KB
.
- Starting threads takes up a certain amount of memory (see thread cost below). By default, each thread takes up
-
- If a large number of threads are enabled, large memory space will be occupied and program performance will be reduced.
-
- The more threads,
CPU
The greater the overhead in scheduling threads.
- The more threads,
-
- Programming is more complex (such as communication between threads, multi-threaded data sharing, etc.).
-
Time slice
A time slice is defined as an interval between tasks that the CPU quickly switches between.
-
Single-core CPU A CPU can process only one thread at a time
- In other words, only one time
1
Three threads are executing.
- In other words, only one time
-
Multithreading simultaneous execution:
- is
CPU
Fast switching between multiple threads. CPU
Scheduling threads fast enough creates multithreadingAt the same time
The effect of execution.
- is
-
If the number of threads is very large, the CPU will switch between N threads, consuming a lot of CPU resources.
-
The number of times each thread is scheduled decreases and the execution efficiency of the thread decreases.
Cost of threads
Thread needs to consume the program and system in memory usage and performance.
Each thread needs to allocate memory between the kernel space and the program memory space;
Manage thread and coordinate thread, need to schedule the required core structure, using limited memory stored in the kernel;
The stack space for the threads and the data for each thread are stored in the program’s memory space;
Most structures are created and initialized at —- when the thread is first created, a process that is relatively expensive due to its interaction with the kernel.
The following table quantifies the approximate cost of creating a new user-level thread in the application. Some of these costs are configurable, such as the amount of stack space allocated to secondary threads. The time cost of creating a thread is a rough approximation and should only be used in relative comparison with one another. Thread creation times vary widely, depending on processor load, computer speed, and the amount of system and program memory available.
Thread creation cost:
Multi-thread technical scheme
Bridge between C and OC
__bridge
Only type conversions are performed, but object (memory) management is not modified.__bridge_retained
(Also availableCFBridgingRetain
) will beObjective-C
Object is converted toCore Foundation
At the same time, the object (memory) management power to us, need to use laterCFRelease
Or related methods to free objects.__bridge_transfer
(Also availableCFBridgingRelease
) will beCore Foundation
Object is converted toObjective-C
Object, and give the object (memory) management rightsARC
.
Thread life cycle
-
New: instantiate thread object;
-
Ready: the thread object calls the start method, adds the thread object to the schedulable thread pool, and waits for the CPU to call it (the call to the start method does not execute immediately, but enters the ready state, after the CPU scheduling, will enter the running state);
-
Run: The CPU is responsible for scheduling the execution of threads in the schedulable thread pool. The state of a thread may switch back and forth between ready and run before its execution is complete. The CPU is responsible for this change.
-
Blocking: When a predetermined condition is met, sleep, or synchronization lock, can be used to block thread execution. When sleep is entered, the thread is re-added to the ready. The following are the NSThread apis for setting sleep times:
sleepUntilDate
: blocks the current thread until the specified time, that is, sleep until the specified time;sleepForTimeInterval
: Hibernates the thread within a specified time interval, that is, specifies the sleep duration.- Synchronization locks:
@synchronized(self)
;
-
Death: divided into two cases:
- Normal death, i.e., completion of the thread;
- An unnatural death is the termination of execution within a thread or in the main thread when a condition is met
exit
Method, etc exit);
Thread Pool Principle
-
Check whether the core thread pool is executing tasks.
- If the return
NO
Create a new worker thread to execute; - If the return
YES
And into the2.
;
- If the return
-
Check whether the thread pool work queue is full:
- If the return
NO
, stores the task to a work queue and waitsCPU
scheduling - If the return
YES
And into the3.
;
- If the return
-
Check whether all threads in the thread pool are in the executing state.
- If the return
NO
To schedule free threads in the schedulable thread pool to perform tasks - If the return
YES
And into the(4)
;
- If the return
-
(4) It is carried out by saturation strategy, which is divided into the following four rejection strategies:
AbortPolicy
: throwRejectedExecutionExeception
Exception: Prevents the system from running properlyCallerRunsPolicy
: rolls back the task to the callerDisOldestPolicy
: Drop the most waiting taskDisCardPolicy
: Directly discards the task
RejectedExecutionHandler interface for the four rejection policies.
Analysis of interview questions
What are the factors that affect the speed of task execution
This problem can be analyzed from the following dimensions: CPU scheduling, task complexity, task priority, thread status.
In iOS, threadPriority is deprecated and replaced by qualityOfService (NSQualityOfService).
typedef NS_ENUM(NSInteger, NSQualityOfService) { NSQualityOfServiceUserInteractive = 0x21, NSQualityOfServiceUserInitiated = 0x19, NSQualityOfServiceUtility = 0x11, NSQualityOfServiceBackground = 0x09, NSQualityOfServiceDefault = 1} API_AVAILABLE (macos (10.10), the ios (8.0), watchos (2.0), tvos (9.0));Copy the code
-
NSQualityOfService: specifies the quality of service. Used to indicate the nature of the work and its importance to the system. When there is contention for resources, use high-quality services to obtain more resources than use low-quality service classes
-
NSQualityOfServiceUserInteractive: used for directly relates to provide interactive UI work. For example: handling control events or drawing on the screen;
-
NSQualityOfServiceUserInitiated: used to perform the job by explicit user requests, and in order to allow further user interaction, must show the results of these work immediately. For example, load the mail after the user selects the mail in the mail list.
-
NSQualityOfServiceUtility: used to perform the user is unlikely to wait for the result of work immediately. This work may be requested by the user or may be initiated automatically, and often operates on a time scale visible to the user using non-modal progress indicators. For example: regular content update or batch file operation, such as media import;
-
NSQualityOfServiceBackground: for the user initiated or invisible work. Often, the user does not even know that this work is being done. For example: prefetching content, searching indexes, backing up or synchronizing data with external systems;
-
NSQualityOfServiceDefault: said there was no clear quality of service information. Wherever possible, the appropriate quality of service is determined based on available resources. Otherwise, use NSQualityOfServiceUserInteractive and NSQualityOfServiceUtility between service quality levels.
-
Priority inversion
There are two types of threads:
-
IO intensive, frequently waiting threads;
-
Cpu-intensive threads that rarely wait;
It is easier to increase thread priority for IO intensive than CPU intensive.
-
I(Input Input)/O(Output Output) operation is the slowest and waits frequently. If its priority is low, it is easy to be eliminated by saturation strategy.
-
To avoid this, when the CPU detects a thread that is waiting frequently, it increases its priority, thus increasing the likelihood that the thread will be executed.
Priority factors
-
User-specified thread quality of service;
-
Increases or decreases depending on the frequency of thread waiting;
-
Raises the priority of a thread that has not executed for a long time.
Thread safety
When multiple threads access the same block of resources at the same time, resource looting is likely to occur, resulting in data confusion and data security problems. The following solutions are available:
-
Mutex (synchronized) : @synchronized
-
spinlocks
Most commonly, when multi-window tickets are sold, as shown in the figure below, there is a resource grab, in which case our normal operation is locking.
The mutex
-
Used to protect critical sections and ensure that only one thread can execute at a time;
-
If there is only one place in the code that needs to be locked, use self most of the time to avoid creating a separate lock object.
-
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.
Considerations for using mutex:
-
The locking range of a mutex should be as small as possible. The greater the locking range, the worse the efficiency.
-
Any NSObject that can be locked;
-
The lock object must be accessible to all threads.
spinlocks
-
A spin lock is similar to a mutex, but instead of blocking the thread by sleeping, it blocks in a busy (spinning) state until the lock is acquired.
-
Use scenario: If the lock is held for a short time and the thread does not want to spend too much money on rescheduling, you need to use the spin lock. The property modifier atomic has a spin lock of its own.
-
With the addition of spin lock, when a new thread accesses the code, if it finds that another thread is locking the code, the new thread will use an infinite loop to wait for the completion of the locked code execution, that is, it keeps trying to execute the code, which consumes performance.
The similarities and differences between spin locks and mutex
Similarities:
- At the same time, only one thread can execute the task, that is, the corresponding synchronization function is guaranteed.
Difference:
-
Mutex: Another thread is found executing, the current thread sleeps (that is, is ready), and enters pending execution, that is, suspends. Wait for another thread to open, then wake up to execute;
-
Spin lock: It is found that other threads are executing, and the current thread is in a busy state, which consumes high performance.
Usage Scenarios:
-
According to the complexity of the task, different locks are used, but when the judgment is incomplete, mutex is used to deal with it.
-
If the current task state is short and neat, use the spin lock.
-
Instead, use mutex.
atomic
& nonatomic
atomic
withnonatomic
The role of
Atomic and nonatomic modifiers are used to modify attributes, with the following specialities:
-
Atomic is an atomic property, intended for multithreaded development, and is the default property, which consumes a lot of resources
-
Just add a lock (spin lock) to setter methods to ensure that only one thread writes to the property at a time.
-
The thread processing technology of single-thread write and multi-thread read at the same time;
-
Used in Mac development.
-
-
Nonatomic is a nonatomic property suitable for mobile devices with small memory
-
No lock, high performance;
-
Commonly used in mobile development.
-
However,iOS
Official advice:
- All properties are declared as
nonatomic
To prevent multiple threads from grabbing the same resource. - Try to transfer the service logic of locking and resource snatching to the server to reduce the pressure on mobile clients.
atomic
withnonatomic
The difference between
-
atomic
-
Atomic property (thread-safe), designed for multithreading, default;
-
Ensure that only one thread can write at a time, but multiple threads can write at a time.
-
Atomic has a lock of its own (spinlock) that supports single-write, multiple-read. A single thread writes and multiple threads can read;
-
Thread safety, which consumes a lot of resources.
-
-
nonatomic
-
Non-atomic properties;
-
Non-thread-safe, suitable for mobile devices with small memory.
-
Source code analysis
Objc = objC; objc_setProperty = objC;
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { bool copy = (shouldCopy && shouldCopy ! = MUTABLE_COPY); bool mutableCopy = (shouldCopy == MUTABLE_COPY); reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy); }Copy the code
ReallySetProperty ();
atomic
Embellishment, addedspinlock_t
Lock operation;- so
atomic
It’s a sign, not a lock. whileatomic
The so-called spin lock is implemented by the underlying code.
The relationship between threads and runloops
-
Runloops correspond to threads one by one, with one Runloop corresponding to one core thread. Core because runloops can 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 a thread’s Runloop is enabled, the thread will go to sleep after executing a task. When a task is available, it will be woken up to execute the task.
-
The Runloop is created on the first fetch and destroyed at the end of the thread;
-
The main thread Runloop, created by default when the program starts;
-
For child threads, runloops are lazily loaded and only created when we use them. So, when a child thread uses a timer, make sure the child thread’s Runloop is created, otherwise the timer cannot be called back.
Communication between threads
At some point, threads may need to process new work requests or report their progress to your application’s main thread. In these cases, communication between threads becomes necessary, and you need a way to get information from one thread to another.
There are many ways to communicate between threads, each with its own advantages and disadvantages.
Configuring Thread local storage lists the most common communication mechanisms you can use in OS X. In addition to message queues and Cocoa distributed objects, these technologies are also available in iOS.
In ascending order of communication mechanism complexity: