This is the 10th day of my participation in the August More text Challenge. For details, see: August More Text Challenge


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;

/** 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]; = @" Running thread";
        [self.p_thread start];
        NSLog(@"%@ executing";
        // 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 thancorePoolSize(number of core threads), then create it immediatelyCore threadRun 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.

  • AbortPolicyDirect sellingRejectedExecutionExeceptionExceptions to prevent the system from working properly
  • CallerRunsPolicyThe task falls back to the caller
  • DisOldestPolicyGet rid of the tasks you’ve been waiting for the longest
  • DisCardPolicyDiscard 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:

  • IOIntensive (frequent waiting threads)
  • CPUIntensive (rarely waiting)
  • IO CPUIt’s easier to get a higher priority
  • Starve to death: have been waiting for execution, discarded
  • Scheduling: priority andCPUScheduling 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];
    // - in the application, collect error log, can record the working thread!
    // Otherwise, it is difficult to determine which thread is causing the problem! = @" 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 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.


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 method
  • nonatomic: nonatomic property that does not lock setter methods

nonatomicandatomicThe 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 asnonatomic.
  • Try to avoid multithreadingrobSame resource.
  • As far as possible willlock,Resources plunderedTo the server side to handle the business logic, reduce the pressure on the mobile client.

atomicThe underlying implementation of spin lock

We’re exploringThe nature of the classFor the properties of the classsetterMethod, the system will have a layerobjc_setPropertyEncapsulation (libobjc.dylibThe source code)The bottom layer will callreallySetPropertyMethod, in the implementation of the method, for atomic properties, addedspinlockThe 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);

    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];
        oldValue = *slot;
        *slot = newValue;        

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

  1. runloopThere is a one-to-one correspondence with threads, onerunloopA thread that corresponds to a core. Why is it corerunloopCan 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 🌹