This article is purely personal reading notes.

This paper is mainly divided into the following modules:

Thread life cycle: new – ready – run – block – death Pthread, NSThread, GCD, NSOperation thread safety problem 5, NSThread usage 6, GCD understanding and use 7, NSOperation understanding and use

First, knowledge points:

  • CPU time slice, each obtaining CPU task can only run for a specified time slice.
  • A thread is a piece of code and runtime data.
  • Each application is a process.
  • All tasks of a process are performed in threads, and each process has at least one thread (the main thread).
  • Main thread: Handles the UI, and all UI updates must be performed on the main thread. Do not put time-consuming operations in the main thread, will block the interface.
  • Multithreading: A CPU can only handle one thread at a time, but the CPU can switch between multiple threads quickly enough to create the illusion of multiple threads executing together.

We use multithreading for the purpose of putting time-consuming operations in the background!

Second, the life cycle of threads: new – ready – run – block – death

Create: instantiate a thread object. 2. Ready: Send a start message to the thread object, and the thread object is added to the schedulable thread pool for CPU scheduling. Run: executed by the CPU. Before execution is complete, the state may switch back and forth between ready and run. The CPU is in charge. 4. Block: When a predetermined condition is met, sleep or lock can be used to block thread execution. * sleepForTimeInterval, * sleepUntiDate, * @synchronized(self) :(mutexes) Death: * Normal death, the thread completes execution. * Unnatural death, terminating execution within a thread when a condition is met/terminating a thread object in the main thread. Thread exit and cancel: * [thread exit]: Once a thread is forcibly terminated, all subsequent code is not executed. * [Thread Cancel]: The default (delayed cancellation), which sets the cancellation flag for pthreads. Pthreads often check to see if they have a cancellation request.Copy the code

Three, the four schemes of multithreading: Pthread, NSThread, GCD, NSOperation

1. Pthreads: POSIX Threads (Pthreads for short) is the POSIX standard for threads. C language, is a set of universal API, cross-platform Unix/Linux/Windows. The life cycle of a thread is managed by the programmer. 2. NSThread: Object oriented, can directly operate thread objects. The life cycle of a thread is managed by the programmer. 3. GCD: Instead of NSThread, it can make full use of the multi-core of the device and automatically manage the thread life cycle. 4. NSOperation: The bottom layer is GCD, which has more methods than GCD, is more object-oriented, and automatically manages the thread lifecycle.Copy the code

Fourth, thread safety

When multiple threads access the same resource, data corruption and data security issues can easily occur.Copy the code

Solution to multi-thread security problem:

1. Method 1: Mutex (synchronous lock)

Used to secure critical sections to ensure that only one thread accesses data at a time. If only one place in the code needs to be locked, most use self as the lock object to avoid creating a separate lock object. Add mutex to do the 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.

@synchronized {//TO DO}Copy the code

2. Method 1: spin lock

Like a mutex, it does not block the process by sleeping, but by remaining busy (spin) blocked until the lock is acquired. This is used when the lock is held for a short time and the thread does not want to spend a lot of money on rescheduling. Go around in circles.” The difference between a spin lock and a mutex is that a thread applying for a spin lock is not suspended, but in a busy state.

With the addition of a spin lock, when a new thread accesses code, if it finds that another thread is locking code, the new thread will use an infinite loop, waiting for the locked code to complete. This is equivalent to constantly trying to execute code, which is a performance drain. Attribute modifier Atomic has a spin lock of its own.

Attributes modify atomic and nonatomic

Atomic (default) : Atomic (thread-safe), which ensures that only one thread can write at a time (but multiple threads can write at a time). Atomic has its own lock (spinlock), which consumes a lot of resources.

Nonatomic: Non-atomic property (non-thread-safe). Many threads can read and write at the same time. However, it is more efficient and nonatomic is generally used.

5. Use of NSThread

1. NSThread creates a thread

Init way - detachNewThreadSelector create good start automatically after - performSelectorInBackground create good after is started directlyCopy the code
- (void) createNSThread{
   
   NSString *threadName1 = @"NSThread1";
   NSString *threadName2 = @"NSThread2";
   NSString *threadName3 = @"NSThread3";
   NSString *threadNameMain = @"NSThreadMain";
   
   NSThread *thread1 = [[NSThread alloc] initWithTarget:self  
   selector:@selector(doSomething:) object:threadName1];
   [thread1 start];
   
   [NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self   
  withObject:threadName2];
   
   [self performSelectorInBackground:@selector(doSomething:) withObject:threadName3]; // Run on the main thread,waitUntilDone: Whether to block waiting for @selector(do[Something:) has been completed the self performSelectorOnMainThread: @ the selector (doSomething:)   
   withObject:threadNameMain waitUntilDone:YES];
   
}

- (void) doSomething:(NSObject *)object{
   	NSLog(@"% @ : % @", object,[NSThread currentThread]);
}
Copy the code

2. Common class methods of NSThread

  • Return current thread
// currentThread [NSThread currentThread]; NSLog(@"% @",[NSThread currentThread]); <NSThread: 0x608000261380>{number =1, name = main}Copy the code
  • Blocking the dormant
[NSThread sleepForTimeInterval:2]; [NSThread sleepUntilDate:[NSDate date]];Copy the code
  • Class method supplement
// Exit thread [NSThread]exit]; // check whether the current thread is the main thread [NSThread isMainThread]; // Check whether the current thread is threaded [NSThread isMultiThreaded]; NSThread mainThread = [NSThread mainThread];Copy the code
  • Some properties of NSThread
// whether a thread isExecuting thread.isexecuting; // Whether thread. IsCancelled; // Whether thread. IsFinished; // Whether the main thread is thread.isMainThread; The value ranges from 0.0 to 1.0. The default priority is 0.5. 1.0 indicates the highest priority and a high CPU scheduling frequency.Copy the code

Vi. Understanding and Use of GCD

No.1: The characteristics of the COMMUNIST Party of China

  • The GCD automatically uses more CPU cores
  • GCD automatically manages thread lifecycles (creating threads, scheduling tasks, destroying threads, and so on)
  • Programmers just need to tell the CCP how they want to perform tasks without writing any thread management code

No.2: Basic concept of THE COMMUNIST Party of China

Task 2.1.

Task: Simply means to perform an action, in other words, the piece of code you execute in a thread. In GCD it is placed in a block. There are two ways to execute tasks: sync and Async. The main differences between the two are whether to wait for queued tasks to finish and whether to have the ability to start a new thread.

  • Sync Execution:
    • Synchronization Synchronously adds a task to a specified queue. Before completing the added task, the system waits until the task in the queue is complete
    • You can only execute tasks in the current thread and do not have the ability to start new threads.
  • Asynchronous execution:
    • Asynchronously adds a task to the specified queue, which does not wait and can continue to execute the task.
    • You can execute tasks in new threads and have the ability to start new threads.
    • Asynchrony is a byword for multithreading.

    Note: Async has the ability to start a new thread, but it does not have to start a new thread. This depends on the type of queue specified by the task

2.2. The queue

Dispatch Queue: This Queue is a waiting Queue for tasks to be executed. That is, it is used to store tasks. A queue is a special linear table that uses FIFO (first in, first out), meaning that new tasks are always inserted at the end of the queue and read from the head of the queue. Each time a task is read, a task is released from the queue. Structure:

There are two types of queues in GCD: serial queues and concurrent queues. Both comply with the FIFO principle. The main differences between the two are: the execution order is different, and the number of threads started is different.

  • Serial Dispatch Queue:
    • Only one task is executed at a time, allowing tasks to be executed one after another (only one thread is opened).
  • Concurrent Dispatch Queue:
    • Multiple tasks can be executed concurrently. (Multiple threads can be started and tasks can be executed simultaneously).

Note: The concurrency functionality of concurrent queues is only available with asynchronous (dispatch_async) functions

GCD summary: Add tasks (operation blocks to be executed in threads) to queues (create or use global concurrent queues yourself) and specify how to execute tasks (asynchronous or synchronous).

No.3: How to use GCD

  1. Create a queue
  2. The task is appended to the task wait queue, and the system executes the task based on the task type

3.1 Method of creating/obtaining a queue

  • You can usedispatch_queue_createTo create a queue, you need to pass in two parameters, the first parameter represents the queueUnique identifier, used for DEBUG, can be null, the application ID is recommended to use the reverse full domain name; The second parameter saysQueue Task type, serial or concurrent queue.
  • DISPATCH_QUEUE_SERIALRepresents serial queue
  • DISPATCH_QUEUE_CONCURRENTRepresents concurrent queue
// serialQueue dispatch_queue_t serialQueue = dispatch_queue_create("com.leejtom.testQueue", DISPATCH_QUEUE_SERIAL); Dispatch_queue_t concurrentQueue = dispatch_queue_create("com.leejtom.testQueue", 
DISPATCH_QUEUE_CONCURRENT);
Copy the code
  • forSerial queuesThe GCD provides a special kind of serial queue:Main Dispatch Queue.
    • Primary queue replication schedules tasks on the main thread. If there are already tasks running on the main thread, the primary queue will wait until the main thread is idle before scheduling tasks.
    • All tasks placed in the main queue are executed in the main thread.
    • Usually used when returning to the main thread to update the UI.
    • You can usedispatch_get_main_queue()Get the main queue.
Dispatch_queue_t mainQueue = dispatch_get_main_queue();Copy the code
  • forConcurrent queue, GCD provides it by defaultGlobal Dispatch Queue.
    • You can usedispatch_get_global_queueIn order to get.
      • The first parameter: indicates the queue priorityDISPATCH_QUEUE_PRIORITY_DEFAULT
      • Second argument: use 0
Dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
Copy the code

Usually we use both as follows:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{dispatch_async(dispatch_get_main_queue(), ^{// return to main thread for UI operation}); });Copy the code

3.2 Synchronous/Asynchronous/Task and Creation Mode

  • The GCD provides a method for creating synchronous execution tasksdispatch_syncAnd asynchronous execution task creation methodsdispatch_async.
// Execute dispatch_sync(dispatch_get_global_queue(0, 0), ^{//TO DO}); // Dispatch_async (dispatch_get_global_queue(0, 0), ^{//TO DO});Copy the code

3.3 GCD combination:

  1. Synchronous execution + concurrent queue
  2. Asynchronous execution + concurrent queue
  3. Synchronous execution + serial queue
  4. Asynchronous execution + serial queue
  5. Synchronous execution + primary queue
  6. Asynchronous execution + primary queue
The difference between Concurrent queue Serial queues The home side column
Synchronization (sync) No new thread is started,

Serial execution of tasks
No new thread is started,

Serial execution of tasks
Main thread call: deadlock stuck not executing;

Other thread calls: no new thread is opened,

Serial execution of tasks
Asynchronous (async) Start a new thread,

Concurrent execution of tasks
Start a new thread (1),

Serial execution of tasks
No new thread is started,

Serial execution of tasks

No.4. Basic use of GCD

4.1 Synchronous execution + concurrent queue

  • Features: A task is executed in the current thread and no new thread is opened. After one task is executed, the next task is executed.
/** * Synchronous execution + concurrent queue * Features: The task is executed in the current thread, no new thread is opened, the execution of a task, then the next task. */ - (void) syncConcurrent { NSLog(@"currentThread: %@", [NSThread currentThread]);
	NSLog(@"syncConcurrent begin");
	
	dispatch_queue_t concurrentQueue = dispatch_queue_create("leejtom.testQueue", DISPATCH_QUEUE_CONCURRENT);
	
	dispatch_sync(concurrentQueue, ^{
		for(int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // simulate time-consuming operation NSLog(@"task1--%@", [NSThread currentThread]); // Prints the current thread}}); dispatch_sync(concurrentQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task2--%@", [NSThread currentThread]); }}); dispatch_sync(concurrentQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task3--%@", [NSThread currentThread]); }}); NSLog(@"syncConcurrent end");
}

Copy the code

Input results are executed sequentially, both in the main thread:

currentThread: <NSThread: 0x115d0ba00>{number = 1, name = main}

syncConcurrent begin

task1–<NSThread: 0x115d0ba00>{number = 1, name = main}

task1–<NSThread: 0x115d0ba00>{number = 1, name = main}

task2–<NSThread: 0x115d0ba00>{number = 1, name = main}

task2–<NSThread: 0x115d0ba00>{number = 1, name = main}

task3–<NSThread: 0x115d0ba00>{number = 1, name = main}

task3–<NSThread: 0x115d0ba00>{number = 1, name = main}

syncConcurrent end

4.2 Asynchronous execution + concurrent queue

  • Features: Multiple threads can be started and tasks can be executed alternately (simultaneously).
/** * Asynchronous execution + concurrent queue * Features: Multiple threads can be started and tasks can be executed alternately (simultaneously). */ - (void) asyncConcurrent { NSLog(@"currentThread: %@", [NSThread currentThread]);
	NSLog(@"asyncConcurrent begin");
	
	dispatch_queue_t concurrentQueue = dispatch_queue_create("leejtom.testQueue", DISPATCH_QUEUE_CONCURRENT);
	
	dispatch_async(concurrentQueue, ^{
		for(int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // simulate time-consuming operation NSLog(@"task1--%@", [NSThread currentThread]); // Prints the current thread}}); dispatch_async(concurrentQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task2--%@", [NSThread currentThread]); }}); dispatch_async(concurrentQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task3--%@", [NSThread currentThread]); }}); NSLog(@"asyncConcurrent end");
}
Copy the code

The result is that multiple threads can be started and tasks can be executed alternately (simultaneously) :

currentThread: <NSThread: 0x113e05aa0>{number = 1, name = main}

asyncConcurrent begin

asyncConcurrent end

task3–<NSThread: 0x113d8a720>{number = 3, name = (null)}

task1–<NSThread: 0x113d89e40>{number = 4, name = (null)}

task2–<NSThread: 0x113d83c60>{number = 5, name = (null)}

task3–<NSThread: 0x113d8a720>{number = 3, name = (null)}

task2–<NSThread: 0x113d83c60>{number = 5, name = (null)}

task1–<NSThread: 0x113d89e40>{number = 4, name = (null)}

In the asynchronous execution + concurrent queue, you can see:

  • In addition to the current thread (the main thread), three more threads are opened and tasks are executed alternately/simultaneously. Asynchronous execution has the ability to start new threads. And concurrent queues can open multiple threads to perform multiple tasks at the same time).
  • All tasks are executed after the printed asyncConcurrent BEGIN and asyncConcurrent End. The current thread does not wait. Instead, it directly starts a new thread and executes tasks in the new thread (asynchronous execution does not wait and can continue executing tasks).

4.3 Synchronous execution + serial queue

  • Features: No new thread will be opened and tasks will be executed in the current thread. Tasks are sequential, one task is executed, and the next is executed.
/** * Synchronous execution + serial queue * Features: new threads will not be started, and tasks will be executed in the current thread. * Tasks are serial, one task is executed before the next one is executed. */ - (void) syncSerial { NSLog(@"currentThread: %@", [NSThread currentThread]);
	NSLog(@"syncSerial begin");
	
	dispatch_queue_t serialQueue = dispatch_queue_create("leejtom.testQueue", DISPATCH_QUEUE_SERIAL);
	
	dispatch_sync(serialQueue, ^{
		for(int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // simulate time-consuming operation NSLog(@"task1--%@", [NSThread currentThread]); // Prints the current thread}}); dispatch_sync(serialQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task2--%@", [NSThread currentThread]); }}); dispatch_sync(serialQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task3--%@", [NSThread currentThread]); }}); NSLog(@"syncSerial end");
}

Copy the code

The output is executed sequentially, all in the main thread:

currentThread: <NSThread: 0x11fe04970>{number = 1, name = main}

syncSerial begin

task1–<NSThread: 0x11fe04970>{number = 1, name = main}

task1–<NSThread: 0x11fe04970>{number = 1, name = main}

task2–<NSThread: 0x11fe04970>{number = 1, name = main}

task2–<NSThread: 0x11fe04970>{number = 1, name = main}

task3–<NSThread: 0x11fe04970>{number = 1, name = main}

task3–<NSThread: 0x11fe04970>{number = 1, name = main}

syncSerial end

4.4 Asynchronous execution + Serial queue

  • New threads are started, but because tasks are serial, one task is executed, and the next is executed
/** * Asynchronous execution + serial queue * Features: New threads are started, but since tasks are serial, one task is executed before the next one is executed. */ - (void) asyncSerial { NSLog(@"currentThread: %@", [NSThread currentThread]);
	NSLog(@"asyncSerial begin");
	
	dispatch_queue_t serialQueue = dispatch_queue_create("leejtom.testQueue",DISPATCH_QUEUE_SERIAL);
	
	dispatch_async(serialQueue, ^{
		for(int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // simulate time-consuming operation NSLog(@"task1--%@", [NSThread currentThread]); // Prints the current thread}}); dispatch_async(serialQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task2--%@", [NSThread currentThread]); }}); dispatch_async(serialQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task3--%@", [NSThread currentThread]); }}); NSLog(@"asyncSerial end");
}
Copy the code

The output is executed sequentially, with different threads:

currentThread: <NSThread: 0x101005730>{number = 1, name = main}

asyncSerial begin

asyncSerial end

task1–<NSThread: 0x1010ab140>{number = 3, name = (null)}

task1–<NSThread: 0x1010ab140>{number = 3, name = (null)}

task2–<NSThread: 0x1010ab140>{number = 3, name = (null)}

task2–<NSThread: 0x1010ab140>{number = 3, name = (null)}

task3–<NSThread: 0x1010ab140>{number = 3, name = (null)}

task3–<NSThread: 0x1010ab140>{number = 3, name = (null)}

  • A new thread is started (asynchronous execution has the ability to start new threads, serial queues only start one thread).
  • All tasks are executed after the printed asyncSerial BEGIN and asyncSerial End (asynchronous execution does not wait and can continue executing the task).
  • Tasks are executed sequentially (only one task is executed in a serial queue at a time, and tasks are executed sequentially one after another).

4.5 Synchronization + Primary Queue

The result of the simultaneous execution + main queue is also different from one thread to another. The deadlock occurs when the main thread is called, but not in other threads.

4.5.1 Synchronization + Primary Queue
  • Main queue: A special serial queue that comes with the GCD
    • All tasks placed in the main queue are executed in the main thread
    • You can usedispatch_get_main_queue()Get the main queue
  • Deadlocks occur, interequivalence freezes and does not execute, program crashes
/** * Synchronous execution + main queue * feature (main thread call) : reciprocity stuck not executing. * Features (called by other threads) : No new threads will be started, one task will be completed, and the next will be executed. */ - (void) syncMain { NSLog(@"currentThread: %@", [NSThread currentThread]);
	NSLog(@"syncMain begin");
	
	dispatch_queue_t mainQueue = dispatch_get_main_queue();
	
	dispatch_sync(mainQueue, ^{
		for(int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // simulate time-consuming operation NSLog(@"task1--%@", [NSThread currentThread]); // Prints the current thread}}); dispatch_sync(mainQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task2--%@", [NSThread currentThread]); }}); dispatch_sync(mainQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task3--%@", [NSThread currentThread]); }}); NSLog(@"syncMain end");
}
Copy the code

Deadlocks occur in the output, interequivalence freezes and does not execute, and program crashes:

currentThread: <NSThread: 0x101201f70>{number = 1, name = main} syncMain begin

(lldb)

This is because by executing the syncMain method on the main thread, we are putting the syncMain task on the main thread queue. Synchronous execution (dispatch_sync) waits for tasks in the current queue to complete. So when we append task 1 to the main queue, task 1 is waiting for the main thread to finish processing the syncMain task. The syncMain task waits for task 1 to complete before continuing.

So the situation is that both the syncMain task and task 1 are waiting for each other to finish. So people are waiting for each other, so it gets stuck, so our task doesn’t work, and the syncMain end doesn’t print.

What if it is not called in the main thread, but in another thread?

4.5.2 Calling in another ThreadSynchronous execution + primary queue

  • It does not start a new thread, execute one task, and then execute the next
// Using the NSThread detachNewThreadSelector method creates the thread, [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];Copy the code

The output

currentThread: <NSThread: 0x121d96740>{number = 3, name = (null)}

syncMain begin

task1–<NSThread: 0x121d0ba20>{number = 1, name = main}

task1–<NSThread: 0x121d0ba20>{number = 1, name = main}

task2–<NSThread: 0x121d0ba20>{number = 1, name = main}

task2–<NSThread: 0x121d0ba20>{number = 1, name = main}

task3–<NSThread: 0x121d0ba20>{number = 1, name = main}

task3–<NSThread: 0x121d0ba20>{number = 1, name = main}

syncMain end

Use the synchronous execution + main queue column in other threads to see:

All tasks are executed in the main thread (not the current thread), no new threads are opened (all tasks placed in the main queue are executed in the main thread). All tasks are executed between the printed syncMain BEGIN and syncMain End (synchronization tasks need to wait for the completion of tasks in the queue). Tasks are executed sequentially (the main queue is a serial queue, where only one task is executed at a time and the tasks are executed sequentially). Why isn’t it stuck now? Because the syncMain task is placed in another thread, tasks 1, 2, and 3 are appended to the main queue, and all three are executed in the main thread. The syncMain task is added to the main queue by another thread. Since there is no task in the main queue, the syncMain task is directly executed. After task 1 is completed, task 2 and task 3 are executed. So there’s no thread stuck here.

4.6 Asynchronous Execution + Primary Queue

  • Tasks are executed only in the main thread, after one task is executed, the next task is executed.
/** * asyncMain {NSLog(@); /** * asyncMain (@); /** * asyncMain (@)"currentThread: %@", [NSThread currentThread]);
	NSLog(@"asyncMain begin");
	
	dispatch_queue_t mainQueue = dispatch_get_main_queue();
	
	dispatch_async(mainQueue, ^{
		for(int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // simulate time-consuming operation NSLog(@"task1--%@", [NSThread currentThread]); // Prints the current thread}}); dispatch_async(mainQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task2--%@", [NSThread currentThread]); }}); dispatch_async(mainQueue, ^{for (int i = 0; i < 2; ++i) {
			[NSThread sleepForTimeInterval:2];
			NSLog(@"task3--%@", [NSThread currentThread]); }}); NSLog(@"asyncMain end");
}
Copy the code

Output result:

currentThread: <NSThread: 0x100e05980>{number = 1, name = main}

asyncMain begin

asyncMain end

task1–<NSThread: 0x100e05980>{number = 1, name = main}

task1–<NSThread: 0x100e05980>{number = 1, name = main}

task2–<NSThread: 0x100e05980>{number = 1, name = main}

task2–<NSThread: 0x100e05980>{number = 1, name = main}

task3–<NSThread: 0x100e05980>{number = 1, name = main}

task3–<NSThread: 0x100e05980>{number = 1, name = main}

  • All tasks are executed in the current thread (main thread) and no new thread is started (asynchronous execution has the ability to start threads, but because it is the main queue, all tasks are in the main thread).
  • All tasks are executed after syncconcurrent-begin and Syncconcurrent-end are printed (asynchronous execution does not wait and the task continues). Tasks are executed sequentially (because the main queue is a serial queue, only one task is executed at a time, and tasks are executed sequentially one after another).

5. Communication between GCD threads

In iOS development, we usually do UI refreshes in the main thread, such as clicking, scrolling, dragging and so on. We usually put some time-consuming operations on other threads, such as image downloading, file uploading, etc. Communication between threads is used when we sometimes need to return to the main thread after another thread has completed a time-consuming operation.

/** * communication between threads */ - (void)communication {// Dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); Dispatch_queue_t mainQueue = dispatch_get_main_queue(); Dispatch_async (queue, ^{// Async adds tasksfor(int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // simulate time-consuming operation NSLog(@"1 - % @",[NSThread currentThread]); Dispatch_async (mainQueue, ^{NSThread sleepForTimeInterval:2]; // simulate time-consuming operation NSLog(@"2 - % @",[NSThread currentThread]); // Prints the current thread}); }); }Copy the code

1—<NSThread: 0x159e97a40>{number = 3, name = (null)}

1—<NSThread: 0x159e97a40>{number = 3, name = (null)}

2—<NSThread: 0x159e08c20>{number = 1, name = main}

  • You can see that the other thread executes the task first and then returns to the main thread to perform the corresponding operation for the main thread.

7. Understanding and use of NSOperation

1. The introduction of NSOperation

NSOperation is a higher level of encapsulation based on GCD, and NSOperation needs to work with NSOperationQueue to implement multi-threading.

NSOperatino implements multithreading as follows:

  1. Create a task: Encapsulate the operations to be performedNSOperationIn the object.
  2. Create queue: Create a queueNSOperationQueue.
  3. To add a task to a queueNSOperationObject added toNSOperationQueueIn the.

Note that NSOperation is an abstract class, and you need to use its subclasses in three ways:

  1. Use subclass NSInvocationOperation
  2. Use the subclass NSBlockOperation
  3. Define subclasses derived from NSOperation that encapsulate tasks by implementing internal corresponding methods.

2. Three creation modes of NSOperation

  1. NSInvocationOperation

  2. NSBlockOperation

  3. Apply subclasses derived from NSOperation

2.1 Use of NSInvocationOperation

Create the NSInvocationOperation object and associate the method, then start.

- (void) createNSOperation {
	NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
	
	[invocationOperation start];
}

- (void) invocationOperation {
	NSLog(@"currentThread: %@", [NSThread currentThread]);
}
Copy the code

The program is executed on the main thread without opening a new thread:

currentThread: <NSThread: 0x143d0b880>{number = 1, name = main}

2.2 throughaddExecutionBlockThis method allows NSBlockOperation to be multithreaded.

- (void) testNSBlockOperationExecution {
	NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
		NSLog(@"main task = >currentThread: %@", [NSThread currentThread]);
	}];
	
	[blockOperation addExecutionBlock:^{
		NSLog(@"task1 = >currentThread: %@", [NSThread currentThread]);
	}];
	
	[blockOperation addExecutionBlock:^{
		NSLog(@"task2 = >currentThread: %@", [NSThread currentThread]);
	}];
	
	[blockOperation addExecutionBlock:^{
		NSLog(@"task3 = >currentThread: %@", [NSThread currentThread]);
	}];
	[blockOperation start];
}
Copy the code

Result: When NSBlockOperation is created, the tasks in the block are executed on the main thread, while the tasks added with addExecutionBlock are executed on the child thread.

main task = >currentThread: <NSThread: 0x10160b760>{number = 1, name = main}

task1 = >currentThread: <NSThread: 0x101733690>{number = 3, name = (null)}

task2 = >currentThread: <NSThread: 0x10160b760>{number = 1, name = main}

task3 = >currentThread: <NSThread: 0x101733690>{number = 3, name = (null)}

First we define a class that inherits from NSOperation, and then we override its main method.

//  JTOperation.m
#import "JTOperation.h"
@implementation JTOperation

- (void)main {
	for (int i = 0; i < 3; i++) {
		NSLog(@"Subclass of NSOperation: %@",[NSThread currentThread]); }} // call - (void)testJTOperation {
	JTOperation *operation = [[JTOperation alloc]init];
	[operation start];
}
Copy the code

The result is executed on the main thread:

NSOperation subclass:

{number = 1, name = main} 0x101605ba0>{number = 1, name = main} Subclass of NSOperation:

{number = 1, name = main}

3. The queue NSOperationQueue

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

  • The main queue, on which tasks are executed on the main thread.
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
Copy the code
  • Other queues (non-main queues), tasks added to ‘non-queues’ are concurrent by default, enabling multi-threading.
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
Copy the code

Note:

  1. Non-primary queue columns (other queues) can be serial or parallel.
  2. The NSOperationQueue has a parameter called maximum concurrency:
@property NSInteger maxConcurrentOperationCount;
Copy the code
  1. MaxConcurrentOperationCount defaults to 1, direct concurrent execution, so join the task of ‘the queue is by defaultConcurrency, enable multithreading.
static const NSInteger NSOperationQueueDefaultMaxConcurrentOperationCount = -1;
Copy the code
  1. When the maxConcurrentOperationCount for 1, said don’t open threads, namelyserial.
  2. When maxConcurrentOperationCount is greater than 1Concurrent execution.
  3. System has a limit to the maximum concurrency, so even if a programmer set maxConcurrentOperationCount is very big, the system will automatically adjust. So it doesn’t make sense to set the maximum number of concurrent requests to very high.

4. NSOperation + NSOperationQueue

Adding tasks to a queue, that’s the normal way to use NSOperation.

  • AddOperation Adds a task to the queue
- (void)testNSOperationQueue {// Create queue, default concurrent NSOperationQueue *queuq = [[NSOperationQueue alloc] init]; NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAddOperation) object:nil]; NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 3; i++) {
			NSLog(@"NSBlockOperation: %@", [NSThread currentThread]); }}]; //addOperation [queuq addOperation:invocationOperation]; [queuq addOperation:blockOperation]; } - (void)operationAddOperation { NSLog(@"NSInvocationOperation: %@", [NSThread currentThread]);
}
Copy the code

The result of the run is that the task is indeed executed in a child thread.

NSInvocationOperation: <NSThread: 0x101a42bf0>{number = 3, name = (null)}

NSBlockOperation: <NSThread: 0x101a42bf0>{number = 3, name = (null)}

NSBlockOperation: <NSThread: 0x101a42bf0>{number = 3, name = (null)}

NSBlockOperation: <NSThread: 0x101a42bf0>{number = 3, name = (null)}

  • Using the maximum number of concurrent to achieve serial use queue attributes maxConcurrentOperationCount (maximum concurrency) to implement the serial, value need to set it to 1 is ok, here we verify through the code.
- (void)testMaxConcurrentOperationCount {
	NSOperationQueue *queue = [[NSOperationQueue alloc]init];
	
	queue.maxConcurrentOperationCount = 1;
	//	queue.maxConcurrentOperationCount = 2;
	[queue addOperationWithBlock:^{
		for (int i = 0; i < 3; i++) {
			NSLog(@"task1: %@",[NSThread currentThread]); }}]; [queue addOperationWithBlock:^{for (int i = 0; i < 3; i++) {
			NSLog(@"task2: %@",[NSThread currentThread]); }}]; [queue addOperationWithBlock:^{for (int i = 0; i < 3; i++) {
			NSLog(@"task3: %@",[NSThread currentThread]); }}]; }Copy the code

When the maximum number of concurrent tasks is 1, although the thread is started, the tasks are executed sequentially, so serial is implemented:

task1: <NSThread: 0x11be67dc0>{number = 3, name = (null)}

task1: <NSThread: 0x11be67dc0>{number = 3, name = (null)}

task1: <NSThread: 0x11be67dc0>{number = 3, name = (null)}

task2: <NSThread: 0x11bdb1bf0>{number = 4, name = (null)}

task2: <NSThread: 0x11bdb1bf0>{number = 4, name = (null)}

task2: <NSThread: 0x11bdb1bf0>{number = 4, name = (null)}

task3: <NSThread: 0x11be67dc0>{number = 3, name = (null)}

task3: <NSThread: 0x11be67dc0>{number = 3, name = (null)}

task3: <NSThread: 0x11be67dc0>{number = 3, name = (null)}

When the maximum number of concurrent tasks is 2, we find that the task is executed concurrently:

task1: <NSThread: 0x10077ca60>{number = 3, name = (null)}

task2: <NSThread: 0x10077d3e0>{number = 4, name = (null)}

task2: <NSThread: 0x10077d3e0>{number = 4, name = (null)}

task2: <NSThread: 0x10077d3e0>{number = 4, name = (null)}

task1: <NSThread: 0x10077ca60>{number = 3, name = (null)}

task1: <NSThread: 0x10077ca60>{number = 3, name = (null)}

task3: <NSThread: 0x10077d3e0>{number = 4, name = (null)}

task3: <NSThread: 0x10077d3e0>{number = 4, name = (null)}

task3: <NSThread: 0x10077d3e0>{number = 4, name = (null)}

5. Perform other operations of NSOperation

  • Unqueue all operations of NSOperationQueue, NSOperationQueue object method
- (void)cancelAllOperations
Copy the code
  • Cancel an operation of NSOperation, NSOperation object method
- (void)cancel
Copy the code
  • To pause or resume a queue
// Suspend queue [queue]setSuspended:YES];
Copy the code
  • Determines whether the queue is paused
- (BOOL)isSuspended
Copy the code

Note: Pause and cancel do not cancel the current action immediately, but wait for the current action to complete the execution of no new action.

6. NSOperation operation dependency

NSOperation has a very useful method, which is operation dependency. One operation (Operation2) depends on another operation (Operation1), and operation2 can only be executed when Operation1 is complete. Then it is time for the dependency to kick in.

- (void)testAddDependency {
	NSOperationQueue *queue = [[NSOperationQueue alloc]init];
	
	NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
		for (int i = 0; i < 3; i++) {
			NSLog(@"operation1: %@",[NSThread currentThread]); }}]; NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 3; i++) {
			NSLog(@"operation2: %@",[NSThread currentThread]); }}]; [operation2 addDependency:operation1]; [queue addOperation:operation1]; [queue addOperation:operation2]; }Copy the code

Output: Operation 2 is always executed after operation 1, successfully verifying the above statement.

operation1: <NSThread: 0x103d07d40>{number = 3, name = (null)}

operation1: <NSThread: 0x103d07d40>{number = 3, name = (null)}

operation1: <NSThread: 0x103d07d40>{number = 3, name = (null)}

operation2: <NSThread: 0x103d07d40>{number = 3, name = (null)}

operation2: <NSThread: 0x103d07d40>{number = 3, name = (null)}

operation2: <NSThread: 0x103d07d40>{number = 3, name = (null)}

Article source code GitHub address

IOS Multithreading full set of iOS multithreading: “GCD” a detailed summary of Linux thread termination functions comparison description join, cancel, kill, exit, etc