This is the 10th day of my participation in the August More text Challenge. For details, see: August More Text Challenge
review
In the last post, we learned a lot about processes and threads, so this post will continue!
1. Thread life cycle
In program development, there is a term — life cycle, we all know that APP has a life cycle, so what is the life cycle of a thread?
- Thread life cycle
The thread life cycle consists of five phases:
-
Create: Creates a new thread using the create thread function.
-
Ready: After the thread is created, the start method is called and the thread is in a wait state, waiting for the CPU time allocation to execute.
-
Run: When a ready thread is scheduled and gets CPU resources, it enters the running state. The run method defines the operations and functions of the thread.
-
Block: When the thread is running, it may become blocked for some reason, such as sleep or waiting for a synchronization lock. The thread is removed from the schedulable pool and is blocked. When sleep arrives, it acquires the synchronization lock and is added to the schedulable pool again. Instead of executing the run method immediately, the awakened thread waits again for the CPU to allocate resources to run.
-
Destruct: If the thread finishes executing normally, or if it is forced to terminate prematurely or abnormally, then the thread is destroyed, freeing resources.
-
The thread life cycle flows roughly as follows:
- Thread state walkthrough method
@interface ViewController(a)
@property (nonatomic.strong) NSThread *p_thread;
@end
/** Thread state drill method */
- (void)testThreadStatus{
NSLog(@"%d %d %d".self.p_thread.isExecuting, self.p_thread.isFinished, self.p_thread.isCancelled);
// Life cycle
if ( self.p_thread == nil || self.p_thread.isCancelled || self.p_thread.isFinished ) {
self.p_thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
self.p_thread.name = @" Running thread";
[self.p_thread start];
}else{
NSLog(@"%@ executing".self.p_thread.name);
// You can set the popup --> here directly to blank
[self.p_thread cancel];
self.p_thread = nil; }}Copy the code
2. Running policy of the thread pool
Thread pool running policy
There is also a policy for thread execution. See the following figure for a thread pool:
If the queue is full and the number of threads running is less than the maximum number of threads, new tasks will be created directly by non-core threads to complete the work.
- When the thread pool is created, there are no threads in it. The task queue is passed in as an argument. However, even if there are tasks in the queue, the thread pool does not immediately execute them.
When there is a task, the thread pool makes the following judgment:
- If the number of threads running is less than
corePoolSize
(number of core threads), then create it immediatelyCore thread
Run the task.
If the number of running threads is greater than or equal to the corePoolSize, put the task in the queue.
-
If the queue is full and the number of threads running is less than maximumPoolSize, create a non-core thread to run the task immediately.
-
If the queue is full and the number of threads running is greater than or equal to maximumPoolSize, the thread pool saturation policy will handle it.
-
When a thread completes a task, it takes the next task off the queue to execute.
-
When a thread has nothing to do for a certain amount of time (timeout), the thread pool determines that if the number of threads currently running is greater than the corePoolSize, the thread is stopped. So after all the tasks of the thread pool are done, it eventually shrinks to the size of corePoolSize.
Saturated strategy
If the queues in the thread pool are full and the number of threads running is greater than or equal to the maximum number of threads in the current thread pool, the saturation policy is processed.
AbortPolicy
Direct sellingRejectedExecutionExeception
Exceptions to prevent the system from working properlyCallerRunsPolicy
The task falls back to the callerDisOldestPolicy
Get rid of the tasks you’ve been waiting for the longestDisCardPolicy
Discard tasks directly
3. Spin locks and mutex locks
Factors affecting the execution speed of tasks:
CPU
- Task complexity
- Priority of tasks
- Thread state
Priority reversal:
IO
Intensive (frequent waiting threads)CPU
Intensive (rarely waiting)IO
比CPU
It’s easier to get a higher priority- Starve to death: have been waiting for execution, discarded
- Scheduling: priority and
CPU
Scheduling is also related
Priority factors:
- User-specified priority –>
threadPriority
// The main thread is 512K
NSLog(@"%@ %zd K %d"[NSThread currentThread], [NSThread currentThread].stackSize / 1024[NSThread currentThread].isMainThread);
NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(eat) object:nil];
// 1.name - in the application, collect error log, can record the working thread!
// Otherwise, it is difficult to determine which thread is causing the problem!
t.name = @" Eating thread";
//This value must be in bytes and a multiple of 4KB.
t.stackSize = 1024*1024;
t.threadPriority = 1;
[t start];
Copy the code
threadPriority
ThreadPriority replaced by qualityOfService(NSQualityOfService)
typedef NS_ENUM(NSInteger.NSQualityOfService) {
NSQualityOfServiceUserInteractive = 0x21.NSQualityOfServiceUserInitiated = 0x19.NSQualityOfServiceUtility = 0x11.NSQualityOfServiceBackground = 0x09.NSQualityOfServiceDefault = - 1
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
Copy the code
- The frequency of waiting
- Not executing for a long time (also increases priority)
Here is a classic example of a threadTicket sales system, is A classic case of thread work execution, if it is A number of Windows to sell tickets, there will be A resource grab, if A window sold A ticket, B window do not know, or at the same time AB window sold the same ticket, so there will be problems, all lock of great significance.
spinlocks
Is a lock used to protect a shared resource shared by multiple threads. Unlike mutex locks, a spin lock iterates to check whether the lock is available in the form of a busy waiting while trying to acquire it. When the previous thread’s task does not finish (locked), the next thread will wait (not sleep); When the task of the previous thread is finished, the next thread will execute immediately.
In a multi-CPU environment, using a spin lock instead of a general mutex can often improve performance for programs with short locks.
Spin lock: OSSpinLock, dispatch_semaphore_t
The mutex
When a thread on the task is not completed (lock) when the next thread to sleep, waiting for the task has been completed, when a thread on the task has been completed under a thread will automatically wake up and then perform a task, the task will not immediately, but be executable status (ready). Mutex: pthread_mutex, @synchronized, NSLock, NSConditionLock, NSCondition, NSRecursiveLock
Features of spinlocks and mutex
-
Spin-locks are busy, which means that when accessing the locked resource, the caller thread does not go to sleep, but loops around until the locked resource releases the lock.
-
The mutex will sleep, which means that when accessing the locked resource, the caller thread will sleep, and the CPU can schedule other threads to work until the locked resource releases the lock. The dormant thread is awakened.
Advantages and disadvantages of spin-locking
- The advantage is that because spinlocks do not cause the caller to sleep, there are no time-consuming operations such as thread scheduling, CPU time slicing, and so on. So spin locks are much more efficient than mutex locks if they can be acquired in a short amount of time.
- The disadvantage is that the spin-lock is always occupying the CPU. It runs spin all the time without acquiring the lock, so it occupies the CPU. If the lock cannot be acquired in a short period of time, the CPU efficiency will be reduced. Spin locks do not implement recursive calls.
Atomic and nonatomic properties
OC has two options for defining attributes: nonatomic and atomic. The default is atomic
atomic
: atomic property, spin lock the setter methodnonatomic
: nonatomic property that does not lock setter methods
nonatomic
andatomic
The contrast of
atomic
: Is thread-safe and consumes a lot of resources.nonatomic
: Non-thread safe, suitable for mobile devices with small memory.
Peacetime development needs attention
- If there is no property to preempt the resource (such as ticket purchase, recharge), all properties are declared as
nonatomic
. - Try to avoid multithreading
rob
Same resource. - As far as possible will
lock
,Resources plundered
To the server side to handle the business logic, reduce the pressure on the mobile client.
atomic
The underlying implementation of spin lock
We’re exploringThe nature of the classFor the properties of the classsetter
Method, the system will have a layerobjc_setProperty
Encapsulation (libobjc.dylibThe source code)The bottom layer will callreallySetProperty
Method, in the implementation of the method, for atomic properties, addedspinlock
The lock
- objc_setProperty_atomic_copy
void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, true.true.false);
}
Copy the code
- reallySetProperty
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy.bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id((*)char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if(! atomic) { oldValue = *slot; *slot = newValue; }else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
Copy the code
Spinlock is a common locking mechanism provided in the Linux kernel. Spinlock is a way to solve resource conflicts by waiting in place. That is, after a thread acquires a Spinlock, another thread expects to acquire the Spinlock, but cannot acquire it and can only spin in place (busy waiting). Because of the busy-wait nature of the spin lock, it is bound to be used in scenarios where a spin lock should not be held for long periods of time (consuming CPU resources).
Attention, attention, attention
Atomic is just an identifier for an atomic property, so atomic is not a Spinlock.
The relationship between thread and Runloop
runloop
There is a one-to-one correspondence with threads, onerunloop
A thread that corresponds to a core. Why is it corerunloop
Can be nested, but there can only be one core, and their relationships are stored in a global
In the dictionary. 2. The runloop is used to manage the thread. When the runloop is enabled, the thread will go into rest state after executing the task, and will wake up to execute the task. 3. The runloop is created when the thread is first fetched and destroyed when the thread ends. 4. For the main thread, the runloop is created by default as soon as the program starts. 5. For the child thread, the runloop is lazily loaded and created only when we use it, so when using the child thread, make sure that the runloop is created, otherwise the timer will not call back.
4. IOS technology solution
Multi-threading has Pthread, NSThread, GCD, NSOperation and other schemes.
The technical solution of iOS is as follows:
For more information on multithreading, go to the Apple documentationThreading Programming Guide More to come
🌹 just like it 👍🌹
🌹 feel have harvest, can come a wave, collect + concern, comment + forward, lest you can’t find me next 😁🌹
🌹 welcome everyone to leave a message to exchange, criticize and correct, learn from each other 😁, improve self 🌹