This chapter content

Various concepts about multithreading.

This ZhangMu

Understand these concepts so that you can follow up on the underlying implementation of multithreading. – LibDispatch will be studied for the underlying GCD implementation (in fact, both iOS and Android multithreading is a wrapper implementation of pThread, because pthread is so perfect, Apple has not developed another one)

Tip: This article concept thing is very much, look easy to nod off

Threads and Processes

Process: An application running on a system. Each process is independent of each other, and each process runs in its own dedicated and protected memory space. (Think APP)

Thread: A thread is the basic execution unit of a process. All tasks of a process are executed in a thread. In order for a process to execute tasks, it must have threads, which means it must have at least one thread. (Think it is the main thread in the APP, etc.)

In addition, threads do not have a separate memory address space, only the process’s. TLS (which I mentioned earlier in the objc_init method) is considered to be the local thread’s temporary space.

To quote from the book “Programmer Self-cultivation” : The local storage space of a thread is the private space that some operating systems provide for a thread alone, but it usually has limited capacity, very little. Does not belong to a thread, but is arranged by the operating system

Relationship between two persons:

  1. Address space: Threads of the same process share the address space of the same process, but processes are independent of the address space
  2. Resource ownership: Threads in the same process share their own resources, such as memory, I/O, and CPU, while resources between processes are independent

supplement

Apps are single-process, but iOS systems are multi-process, so iOS apps cannot communicate with processes. But it’s An Android APP that can communicate between processes.

multithreading

Since iOS apps are single-process, it is necessary to use multiple threads to deal with complex things when time-consuming operations are performed

The principle of multithreading: When we have many tasks to execute, the CPU will schedule the thread to execute the task, for example, thread 1-3, thread 1 is executing the task, then the CPU will schedule thread 2 to schedule the task for thread 2 to execute.

In other words, the CPU is constantly switching back and forth to schedule threads to handle tasks. So multithreading isn’t really concurrent (executing simultaneously at the same time), so to be truly multithreaded you need: multiple cores.

If we want to reduce CPU resources, we must not open too many threads. For example, mobile phones are not as powerful as computers.

Time slice: The interval at which a CPU switches between tasks.

Thread life cycle

1->2->3->4->2. Look at the following concept kind of like this

  1. For example, NSThread *t = [[NSThread alloc] init….]

  2. A thread calls start: [t start] to indicate that the thread has started and is ready. The thread then enters the Runnable state, where it waits for the CPU to schedule, adding it to the pool of schedulable threads. (Note that in this state the CPU does not necessarily execute immediately, just wait for scheduling)

  3. CPU scheduled threads: Threads that are normally scheduled to execute by the CPU are dead (thread destruction) upon completion. If the thread is scheduled by the CPU to execute after blocking into 4

  4. Blocked: When a thread calls sleep or waits for a synchronization lock, etc., the CPU removes the thread from the pool of schedulable threads, and the thread is added back to the pool of schedulable threads upon sleep or when a synchronization lock is acquired. And go back to state 2 (notice I said runnable waiting for dispatch)

Added: We see a lot of locking in objC’s underlying code to prevent multithreaded access

The thread pool

It is common to see that when a thread is created, the results are not in order, but out of order. For example, when thread 1 is executing, it may not be thread 2 to schedule execution, but thread 15, and when the task is scheduled again, it may be thread 8. That is: 1,15,8,2,17. So why?

How a thread pool works: There is a threshold that determines whether the thread pool is full or not. If not, create one. This is wrong and imperfect, see below

  1. Are all core threads executing tasks? If any is available, it will be taken by the CPU from the schedulable thread pool and executed. Otherwise, 2

  2. Is the work queue of the thread pool saturated? If not, add the task to the work queue and schedule the thread to execute. Otherwise, 3

  3. Are all the threads in the thread pool in the execution state? If not, the thread is scheduled to perform the task. The 4

  4. Leave it to the saturation policy

Saturated strategy

The current mission is here, but the mission is not in place, so here we are.

  1. AbortPolicy throw an exception (RejectedExecutionException) prevent normal operation of system. For example, if the above threshold is 100, but the task comes in at 101, then the CPU is overloaded.

  2. CallerRunsPolicy rolls back the task to the scheduler. For example: I can’t carry out task A, I will give it back to you, you can do it at your discretion

  3. DisOldestPolicy drops the longest-waiting task. For example, if a task is not executed for a long time, the CPU will keep increasing the priority of the task, but you still can not execute the task, the CPU can not save you, throw it away

  4. DisCardPolicy Discards the task directly.

The thread with the runloop

Runloop will be explored in a future article

  1. A runloop corresponds to a thread one to one, and a runloop corresponds to a core thread. Why core threads? Because runloops can be nested, but there is only one core, their relationships are stored in a global dictionary.

  2. The runloop manages the thread. When the runloop is enabled, the thread will go to sleep after executing a task, and it will wake up to execute the task

  3. The runloop is created when it 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 be careful when using a timer for the child thread: make sure the runloop is created for the child thread, otherwise the timer will not call back

Questions about multithreading

What are the factors that affect the speed of task execution

1. The CPU scheduling status (for example, the current CPU load), 2. The complexity of the task, 3. The priority of the task, 4. Thread state (number of threads, concurrency, etc.)

For example: go to the bank to deposit money, the bank has 3 Windows (3 threads 1, 2, 3), Lao Wang wants to withdraw money to window 1 (thread 1), to get 1000W (task complexity) so window 1 will execute for a long time. When Lao Li came to save money, he wanted to save 10 billion yuan (task complexity). As soon as the lobby manager saw that it was P (task priority) in VIP, he quickly came up and told the other people to go to another window and let thread 3 deal with Lao Li alone (CPU scheduling) after the execution (thread state).

Priority reversal

There is an unwritten rule that it is easier for IO to be prioritized than CPU. The IO intensive task will definitely be slower than the CPU intensive task, and the IO type will wait too much, so it’s possible that the task will be abandoned to “starve”. So the CPU starts scheduling to give this thread a higher priority, and if it hasn’t been executed it’s going to keep getting higher priority. But DisOldestPolicy will be DisOldestPolicy if it has not been implemented

IO intensive: Indicates frequent waiting threads

CPU intensive: few waiting threads

Priority influencing factors

  1. User-specified priority, for example, iOS NSQualityOfService
  2. Depending on how often you enter the wait state or decrease it
  3. If you do not execute for a long time, the priority will increase. This actually belongs to the second point

Multi-threaded resource sharing (Resource grabbing)

Atomic, if you look at objc source method is that we add spinlock_t to the setter and getter. In fact, spin locks are encapsulated in terms of mutex. We usually use the most development is mutex. An inquiry into the locks will come later

IOS often uses multithreading

The following will directly study the principle of GCD, iOS multithreading we know all the GCD, its use method can see the API or baidu can achieve. In terms of multithreading, you also need to know the communication between threads, and you can find the corresponding API for these

Add the bridge between C and OC

The bridge between C and OC, the bridge between C and SWIFT, the bridge between OC and SWIFT, and the resolution of conflicts between method and function names are all acceptable. But here’s only the first one

The bridge between C and OC, you can think of it as CFFoundation and Foundation.

  1. __bridgeCasts only, but does not modify object (memory) management rights
  2. __bridge_retainedorCFBridgingRetainConvert OC objects to Core Foundation objects, and give us the object (memory) management rights, which will be used for subsequent releaseCFReleaseOr related methods release the object
  3. __bridge_transferorCFBridgingReleaseConvert Core Foundation objects to OC objects and give ARC control of the object (memory)