Share some basic WHMultiThreadDemo for iOS multithreading
directory
- The basic concept of multithreading
- The state and life cycle of threads
- Four solutions for multithreading: pThread, NSThread, GCD, NSOperation
- Thread safety
- NSThread
- GCD
- NSOperation
First, the basic concept of multithreading
The purpose of multithreading is to put time-consuming operations in the background.
** process: ** can be understood as a running application program. It is the basic unit of the system for resource allocation and scheduling. It is the basis of the operating system structure and mainly manages resources. ** thread: ** is the basic execution unit of a process. One process corresponds to multiple threads. ** Main thread: ** processes 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.
A thread is like a carriage on a train, and a process is the train. A car (thread) cannot run without a train (process), and a train (process) has at least one car (main thread). Multithreading can be seen as multiple carriages, and it appears to improve efficiency.
Multithreading improves the overall efficiency of the system by increasing resource utilization.
Second, thread state and life cycle
The life cycle of a thread is: new – ready – run – block – die
New: Instantiate thread object ready: A start message is sent to the thread object, which is added to the pool of schedulable threads waiting for CPU scheduling. Run: The CPU is responsible for scheduling the execution of threads in the schedulable thread pool. Before the thread completes execution, the state may switch back and forth between ready and run. The CPU is responsible for state changes between ready and run, and the programmer cannot interfere with blocking: sleep or locking can be used to block thread execution when a predetermined condition is met. SleepForTimeInterval, sleepUntilDate, @synchronized(self) : (mutex) Dead: the thread is dead and the execution is complete. Unnatural death, aborting execution inside a thread/aborting a thread object in the main thread when a condition is met
There’s also exit and cancel for the thread
[NSThread exit] : Once a thread is forcibly terminated, all subsequent code is not executed. [Thread Cancel] Cancel: The thread is not cancelled directly, only the isCancelled flag is added to the thread object.
Three, the four solutions of multithreading
The four solutions to multithreading are pThread, NSThread, GCD, and NSOperation.
Pthread: Uses C language, is a set of universal API, can be cross-platform Unix/Linux/Windows. The life cycle of a thread is managed by the programmer. NSThread: Object oriented, allows direct manipulation of thread objects. The life cycle of a thread is managed by the programmer. GCD: Instead of NSThreads, it can make full use of the device’s multi-core and automatically manage the thread life cycle. NSOperation: Underneath is GCD, which has a few more methods than GCD, is more object-oriented, and automatically manages the thread lifecycle.
Fourth, thread safety
When multiple threads access the same resource, data corruption and data security issues can easily occur. It’s like several people modifying the same table at the same time, resulting in data confusion.
Solution to multi-thread security problem
** Method 1: ** mutex (synchronous lock)
@synchronized(Lock object) {// The code to lock
}
Copy the code
If there is only one place in the code that 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.
When a new thread accesses the code and finds that another thread is locking the code, the new thread will wait for the locked code to complete in an infinite loop. This is equivalent to constantly trying to execute code, which is a performance drain. Attribute modifier Atomic has a spin lock of its own.
Let’s talk about attribute modifications of nonatomic and atomic
nonatomicThe thread safe property ensures that only one thread can write at a time (but multiple threads can write at the same time). The atomic property has its own lock (spin lock)nonatomic: not thread-safe, but more efficient, in general usenonatomic
Copy the code
5. Use of NSThread
1: NSThread creates a thread
There are three ways to create an NSThread:
- The init method
- DetachNewThreadSelector starts automatically when it is created
- After performSelectorInBackground created is started directly
/** Start */
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething1:) object:@"NSThread1"];
// A thread joins the thread pool and waits for CPU scheduling
[thread1 start];
/** ** /
[NSThread detachNewThreadSelector:@selector(doSomething2:) toTarget:self withObject:@"NSThread2"];
/** create ()
[self performSelectorInBackground:@selector(doSomething3:) withObject:@"NSThread3"];
- (void)doSomething1:(NSObject *)object {
// The parameters passed in
NSLog(@ "% @",object);
NSLog(@ "doSomething1: % @"[NSThread currentThread]);
}
- (void)doSomething2:(NSObject *)object {
NSLog(@ "% @",object);
NSLog(@ "doSomething2: % @"[NSThread currentThread]);
}
- (void)doSomething3:(NSObject *)object {
NSLog(@ "% @",object);
NSLog(@ "doSomething3: % @"[NSThread currentThread]);
}
Copy the code
2: class method of NSThread
- Return current thread
// The current thread
[NSThread currentThread];
NSLog(@ "% @"[NSThread currentThread]);
// If number=1, it is in the main thread, otherwise it is in the child threadPrint result: <NSThread: 0x608000261380>{number = 1, name = main}
Copy the code
- Blocking the dormant
// How long to hibernate
[NSThread sleepForTimeInterval:2];
// Sleep until specified time
[NSThread sleepUntilDate:[NSDate date]];
Copy the code
- Class method supplement
// Exit the thread
[NSThread exit];
// Determine whether the current thread is the master thread
[NSThread isMainThread];
// Check whether the current thread is multithreaded
[NSThread isMultiThreaded];
// The main thread object
NSThread *mainThread = [NSThread mainThread];
Copy the code
3: attributes of NSThread
// Whether the thread is executing
thread.isExecuting;
// Whether the thread is cancelled
thread.isCancelled;
// Whether the thread is complete
thread.isFinished;
// Is the main thread
thread.isMainThread;
The value ranges from 0.0 to 1.0. The default priority is 0.5. 1.0 indicates the highest priority and the CPU scheduling frequency is high
thread.threadPriority;
Copy the code
Vi. Use of the COMMUNIST Party CD
1: Characteristics of the COMMUNIST Party of China
- The GCD automatically uses more CPU cores
- GCD automatically manages thread lifecycles (thread creation, task scheduling, thread destruction, etc.)
- Programmers just need to tell the COMMUNIST party how they want to perform what tasks, without writing any thread management code
2: Basic concept of THE COMMUNIST Party of China
- Block: A task is code to be executed in a thread, wrapped in a block, and then added to the specified execution mode (synchronous execution and asynchronous execution), waiting for the CPU to fetch the task from the queue and put it into the corresponding thread for execution.
- Sync: one after another, the first one is not finished, cannot be executed after, do not open the thread.
- Async: Start more than one new thread. Tasks can be executed at the same time. Asynchrony is a byword for multithreading
- Queue: Queue structure for loading threaded tasks. (The system schedules tasks in queues on a first-in, first-out basis.) There are two types of queues in GCD: serial queues and concurrent queues.
- Concurrent queue: Threads can execute together at the same time. It’s actually the CPU switching quickly between multiple threads. (Concurrency only works with asynchronous (dispatch_async) functions)
- Serial queue: Threads can only execute sequentially.
GCD summary: Add task (operation block to be executed in thread) to queue (create your own or use global concurrent queue) and specify how to execute task (asynchronous dispatch_async, synchronous dispatch_sync)
3: queue creation method
- Create a queue object using dispatch_queue_CREATE, passing in two parameters. The first parameter represents the unique identifier of the queue, which can be null. The second parameter is used to represent either a serial queue (DISPATCH_QUEUE_SERIAL) or a concurrent queue (DISPATCH_QUEUE_CONCURRENT).
// Serial queue
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
// Concurrent queue
dispatch_queue_t queue1 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
Copy the code
There are two other types of GCD queues:
- Main queue: The main queue is responsible for scheduling tasks on the main thread. If there are already tasks running on the main thread, the main queue will wait until the main thread is idle before scheduling tasks. Usually used when returning to the main thread to update the UI. dispatch_get_main_queue()
dispatch_async(dispatch_get_global_queue(0.0), ^ {// Time consuming operations are placed here
dispatch_async(dispatch_get_main_queue(), ^{
// Go back to the main thread for the UI operation
});
});
Copy the code
- Global concurrency queue: The global concurrency queue is a concurrency queue, to make it easier to use multi-threading. dispatch_get_global_queue(0, 0)
// Global concurrent queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// Priority of the global concurrent queue
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // High priority
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // Default (middle) priority
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // Low priority
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // Background priority
//iOS8 started using quality of Service (QOS), now when getting global concurrent queues, you can pass 0 directly
dispatch_get_global_queue(0.0);
Copy the code
4: Synchronous/asynchronous/Task creation mode
Sync is represented by dispatch_sync. Async uses dispatch_async. A task is code that will be executed in a thread, wrapped in a block. The code is as follows:
// Execute tasks synchronously
dispatch_sync(dispatch_get_global_queue(0.0), ^ {// Tasks are placed in this block
NSLog(@" I am a synchronous task");
});
// Execute tasks asynchronously
dispatch_async(dispatch_get_global_queue(0.0), ^ {// Tasks are placed in this block
NSLog(@" I am executing tasks asynchronously");
});
Copy the code
5: The use of GCD
Since there are multiple queues (serial/concurrent/primary) and two modes of execution (synchronous/asynchronous), they can be combined in many ways.
Serial synchronous
Serial asynchronous concurrent Synchronous concurrent asynchronous Primary queue Synchronous Primary queue asynchronous
- Serial synchronous
Finish one task, then proceed to the next. No new thread is started.
/** Serial synchronization */
- (void)syncSerial {
NSLog(@"\n\n************** Serial synchronization ***************\n\n");
// Serial queue
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
// Synchronize execution
dispatch_sync(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Serial sync 1 %@"[NSThreadcurrentThread]); }});dispatch_sync(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Serial sync 2 %@"[NSThreadcurrentThread]); }});dispatch_sync(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Serial sync 3 %@"[NSThreadcurrentThread]); }}); }Copy the code
Input results are executed sequentially, both in the main thread:
Serial synchronous1 <NSThread: 0x60000007c500>{number = 1, name = main} Serial synchronization1 <NSThread: 0x60000007c500>{number = 1, name = main} Serial synchronization1 <NSThread: 0x60000007c500>{number = 1, name = main} Serial synchronization2 <NSThread: 0x60000007c500>{number = 1, name = main} Serial synchronization2 <NSThread: 0x60000007c500>{number = 1, name = main} Serial synchronization2 <NSThread: 0x60000007c500>{number = 1, name = main} Serial synchronization3 <NSThread: 0x60000007c500>{number = 1, name = main} Serial synchronization3 <NSThread: 0x60000007c500>{number = 1, name = main} Serial synchronization3 <NSThread: 0x60000007c500>{number = 1, name = main}
Copy the code
- Serial asynchronous
Start a new thread, but because the tasks are serial, they are still executed sequentially.
/** serial asynchronous */
- (void)asyncSerial {
NSLog(@"\n\n************** serial asynchronous ***************\n\n");
// Serial queue
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
// Synchronize execution
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" serial asynchronous 1 %@"[NSThreadcurrentThread]); }});dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" serial asynchronous 2 %@"[NSThreadcurrentThread]); }});dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" serial asynchronous 3 %@"[NSThreadcurrentThread]); }}); }Copy the code
Input results are executed sequentially, with different threads:
Serial asynchronous1 <NSThread: 0x60000026d740>{number = 3, name = (null)} Serial asynchronous1 <NSThread: 0x60000026d740>{number = 3, name = (null)} Serial asynchronous1 <NSThread: 0x60000026d740>{number = 3, name = (null)} Serial asynchronous2 <NSThread: 0x60000026d740>{number = 3, name = (null)} Serial asynchronous2 <NSThread: 0x60000026d740>{number = 3, name = (null)} Serial asynchronous2 <NSThread: 0x60000026d740>{number = 3, name = (null)} Serial asynchronous3 <NSThread: 0x60000026d740>{number = 3, name = (null)} Serial asynchronous3 <NSThread: 0x60000026d740>{number = 3, name = (null)} Serial asynchronous3 <NSThread: 0x60000026d740>{number = 3, name = (null)}
Copy the code
- Concurrent synchronization
Because it is synchronous, one task is executed before the next. No new thread will be started.
/** Concurrent synchronization */
- (void)syncConcurrent {
NSLog(@"\n\n************** Concurrent synchronization ***************\n\n");
// Concurrent queue
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
// Synchronize execution
dispatch_sync(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Concurrent synchronization 1 %@"[NSThreadcurrentThread]); }});dispatch_sync(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Concurrent synchronization 2 %@"[NSThreadcurrentThread]); }});dispatch_sync(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Concurrent synchronization 3 %@"[NSThreadcurrentThread]); }}); }Copy the code
Input results are executed sequentially, both in the main thread:
Concurrent synchronization1 <NSThread: 0x60000007c500>{number = 1, name = main} Concurrent synchronization1 <NSThread: 0x60000007c500>{number = 1, name = main} Concurrent synchronization1 <NSThread: 0x60000007c500>{number = 1, name = main} Concurrent synchronization2 <NSThread: 0x60000007c500>{number = 1, name = main} Concurrent synchronization2 <NSThread: 0x60000007c500>{number = 1, name = main} Concurrent synchronization2 <NSThread: 0x60000007c500>{number = 1, name = main} Concurrent synchronization3 <NSThread: 0x60000007c500>{number = 1, name = main} Concurrent synchronization3 <NSThread: 0x60000007c500>{number = 1, name = main} Concurrent synchronization3 <NSThread: 0x60000007c500>{number = 1, name = main}
Copy the code
- Concurrent asynchronous
Alternate tasks and enable multithreading.
/** Concurrent asynchronous */
- (void)asyncConcurrent {
NSLog(@"\n\n************** Concurrent asynchrony ***************\n\n");
// Concurrent queue
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
// Synchronize execution
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Concurrent asynchronous 1 %@"[NSThreadcurrentThread]); }});dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" concurrent async 2 %@"[NSThreadcurrentThread]); }});dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" concurrent asynchronous 3 %@"[NSThreadcurrentThread]); }}); }Copy the code
The input result is unordered execution with multiple threads:
Concurrent asynchronous1 <NSThread: 0x60000026d740>{number = 3, name = (null)} Concurrency asynchronous2 <NSThread: 0x60000026dc80>{number = 4, name = (null)} Concurrency asynchronous3 <NSThread: 0x60800026ab40>{number = 5, name = (null)} Concurrency asynchronous1 <NSThread: 0x60000026d740>{number = 3, name = (null)} Concurrency asynchronous2 <NSThread: 0x60000026dc80>{number = 4, name = (null)} Concurrency asynchronous3 <NSThread: 0x60800026ab40>{number = 5, name = (null)} Concurrency asynchronous1 <NSThread: 0x60000026d740>{number = 3, name = (null)} Concurrency asynchronous2 <NSThread: 0x60000026dc80>{number = 4, name = (null)} Concurrency asynchronous3 <NSThread: 0x60800026ab40>{number = 5, name = (null)}
Copy the code
- Synchronize the main queue
If you do this on the main thread, a deadlock will occur and the program will crash.
/** Main queue synchronization */
- (void)syncMain {
NSLog(@ "\ n \ n * * * * * * * * * * * * * * master queue synchronization, on the main thread will deadlock * * * * * * * * * * * * * * * \ n \ n");
/ / the home team
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Main queue sync 1 %@"[NSThreadcurrentThread]); }});dispatch_sync(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Main queue sync 2 %@"[NSThreadcurrentThread]); }});dispatch_sync(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Main queue sync 3 %@"[NSThreadcurrentThread]); }}); }Copy the code
Deadlock caused by synchronization of the main queue:
If main queue synchronization is used on the main thread, tasks are placed in the main thread queue. Synchronization is performed immediately for tasks, so when the first task is placed in the main queue, it will be executed immediately. However, the main thread is currently handling the syncMain method, and the task needs to wait for syncMain to finish executing. When syncMain completes the first task, it waits for the second and third tasks to complete. The syncMain method and the first task then start waiting for each other, creating a deadlock.
- The main queue is asynchronous
Tasks are executed sequentially in the main thread.
/** Main queue asynchronous */
- (void)asyncMain {
NSLog(@"\n\n************** Main queue asynchronous ***************\n\n");
/ / the home team
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Main queue asynchronous 1 %@"[NSThreadcurrentThread]); }});dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Main queue asynchronous 2 %@"[NSThreadcurrentThread]); }});dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" Main queue asynchronous 3 %@"[NSThreadcurrentThread]); }}); }Copy the code
The input results are executed sequentially in the main thread:
The main queue is asynchronous1 <NSThread: 0x60000007c500>{number = 1, name = main} The main queue is asynchronous1 <NSThread: 0x60000007c500>{number = 1, name = main} The main queue is asynchronous1 <NSThread: 0x60000007c500>{number = 1, name = main} The main queue is asynchronous2 <NSThread: 0x60000007c500>{number = 1, name = main} The main queue is asynchronous2 <NSThread: 0x60000007c500>{number = 1, name = main} The main queue is asynchronous2 <NSThread: 0x60000007c500>{number = 1, name = main} The main queue is asynchronous3 <NSThread: 0x60000007c500>{number = 1, name = main} The main queue is asynchronous3 <NSThread: 0x60000007c500>{number = 1, name = main} The main queue is asynchronous3 <NSThread: 0x60000007c500>{number = 1, name = main}
Copy the code
- Communication between GCD threads
During development, uI-related operations are performed on the main thread, and time-consuming operations, such as image file downloads, are usually placed on other threads. When you’re done with time-consuming operations, you need to go back to the main thread for UI processing, which is where communication between threads is used.
- (IBAction)communicationBetweenThread:(id)sender {
/ / asynchronous
dispatch_async(dispatch_get_global_queue(0.0), ^ {// Place time-consuming operations here, such as downloading images. (Use thread sleep for two seconds to simulate time-consuming operation)
[NSThread sleepForTimeInterval:2];
NSString *picURLStr = @"http://www.bangmangxuan.net/uploads/allimg/160320/74-160320130500.jpg";
NSURL *picURL = [NSURL URLWithString:picURLStr];
NSData *picData = [NSData dataWithContentsOfURL:picURL];
UIImage *image = [UIImage imageWithData:picData];
// Go back to the main thread to process the UI
dispatch_async(dispatch_get_main_queue(), ^{
// Add images to the main thread
self.imageView.image = image;
});
});
}
Copy the code
The above code is to download the image in a new thread, and then return to the main thread to display the image.
- The GCD fence
When tasks need to be performed asynchronously, but the tasks need to be split into two groups, the second group of operations can be performed only after the first group is completed. This is where the GCD’s fence method dispatch_barrier_async is used.
- (IBAction)barrierGCD:(id)sender {
// Concurrent queue
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
// Execute asynchronously
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" fence: concurrent asynchronous 1 %@"[NSThreadcurrentThread]); }});dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" fence: concurrent asynchronous 2 %@"[NSThreadcurrentThread]); }}); dispatch_barrier_async(queue, ^{NSLog(@"------------barrier------------%@"[NSThread currentThread]);
NSLog(@"******* execute concurrently asynchronously, but 34 must come after 12 *********");
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" fence: concurrent async 3% @"[NSThreadcurrentThread]); }});dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@" fence: concurrent asynchronous 4 %@"[NSThreadcurrentThread]); }}); }Copy the code
The code above prints the following, with multiple threads started and all tasks performed concurrently and asynchronously. But after the first set is complete, the second set of operations will be performed.
Fence: asynchronous concurrency1 <NSThread: 0x60000026d740>{number = 3, name = (null)} fence: concurrency asynchronous2 <NSThread: 0x60000026e480>{number = 6, name = (null)} fence: concurrency asynchronous1 <NSThread: 0x60000026d740>{number = 3, name = (null)} fence: concurrency asynchronous2 <NSThread: 0x60000026e480>{number = 6, name = (null)} fence: concurrency asynchronous1 <NSThread: 0x60000026d740>{number = 3, name = (null)} fence: concurrency asynchronous2 <NSThread: 0x60000026e480>{number = 6, name = (null)}
------------barrier------------<NSThread: 0x60000026e480>{number = 6, name = (null)} ******* Concurrency is executed asynchronously, but34Must be in12Behind ********* fence: concurrent asynchronous4 <NSThread: 0x60000026d740>{number = 3, name = (null)} fence: concurrency asynchronous3 <NSThread: 0x60000026e480>{number = 6, name = (null)} fence: concurrency asynchronous4 <NSThread: 0x60000026d740>{number = 3, name = (null)} fence: concurrency asynchronous3 <NSThread: 0x60000026e480>{number = 6, name = (null)} fence: concurrency asynchronous4 <NSThread: 0x60000026d740>{number = 3, name = (null)} fence: concurrency asynchronous3 <NSThread: 0x60000026e480>{number = 6, name = (null)}
Copy the code
- The GCD execution is delayed
This method is used when you need to wait a while before executing a piece of code: dispatch_after.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// Execute asynchronously after 5 seconds
NSLog("I've been waiting 5 seconds!");
});
Copy the code
- The GCD implementation code is executed only once
Using dispatch_once ensures that a piece of code is executed only once during application execution. Can be used to design singletons.
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog("I only executed the program once!");
});
Copy the code
- GCD iterates quickly
GCD has a fast iterative method, dispatch_apply, that iterates through multiple numbers at once.
- (IBAction)applyGCD:(id)sender {
NSLog(@"\n\n************** GCD quick iteration ***************\n\n");
// Concurrent queue
dispatch_queue_t queue = dispatch_get_global_queue(0.0);
// dispatch_apply traverses multiple numbers almost simultaneously
dispatch_apply(7, queue, ^(size_t index) {
NSLog(Lh-zd @ "dispatch_apply: % = = = = = = % @",index, [NSThread currentThread]);
});
}
Copy the code
The print result is as follows:
Dispatch_apply:0= = = = = ="NSThread: 0x60000007c500>{number = 1, name = main} dispatch_apply:1= = = = = ="NSThread: 0x60000007c500>{number = 1, name = main} dispatch_apply:2= = = = = ="NSThread: 0x60000007c500>{number = 1, name = main} dispatch_apply:3= = = = = ="NSThread: 0x60000007c500>{number = 1, name = main} dispatch_apply:4= = = = = ="NSThread: 0x60000007c500>{number = 1, name = main} dispatch_apply:5= = = = = ="NSThread: 0x60000007c500>{number = 1, name = main} dispatch_apply:6= = = = = ="NSThread: 0x60000007c500>{number = 1, name = main}
Copy the code
- The GCD queues group
Perform several time-consuming operations asynchronously, and then return to the main thread to perform operations when these operations are complete. A queue group has the following characteristics:
All tasks are executed concurrently (out of order).
All asynchronous functions are added to the queue and then listened to by the queue group. The dispatch_group_notify function is used to listen for completion of the above task and if so, this method is called.
Sample queue group code:
- (void)testGroup {
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0.0), ^ {NSLog(Queue group: There is a time-consuming operation completed!);
});
dispatch_group_async(group, dispatch_get_global_queue(0.0), ^ {NSLog(Queue group: There is a time-consuming operation completed!);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@" Queue group: all the previous time-consuming operations are complete, return to the main thread for related operations");
});
}
Copy the code
The print result is as follows:
Queue group: There is one time-consuming operation completed! Queue group: There is one time-consuming operation completed! Queue group: All the previous time-consuming operations have been completed. Return to the main thread for related operationsCopy the code
7. Use of NSOperation
1: Introduction to NSOperation
NSOperation is a higher level of encapsulation based on GCD, and NSOperation needs to work with NSOperationQueue to implement multi-threading. NSOperation 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.Copy the code
Note that NSOperation is an abstract class, and you need to use its subclasses in three ways:
1. Use subclass NSInvocationOperation Use subclass NSBlockOperation 3. Define subclasses that inherit from NSOperation and encapsulate tasks by implementing internal corresponding methods.Copy the code
2: three creation modes of NSOperation
- The use of NSInvocationOperation
Create the NSInvocationOperation object and associate the method, then start.
- (void)testNSInvocationOperation {
/ / create NSInvocationOperation
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
// Start the operation
[invocationOperation start];
}
- (void)invocationOperation {
NSLog(@"NSInvocationOperation contains tasks that are not enqueued ========%@"[NSThread currentThread]);
}
Copy the code
The program was executed in the main thread and no new thread was opened. And that’s because the use of NSOperation multithreading needs to work with the use of NSOperationQueue, which we’ll talk about later.
NSInvocationOperationContains tasks that are not enqueued ========<NSThread: 0x6000000783c0>{number = 1, name = main}
Copy the code
- The use of NSBlockOperation
Put the task in the block of NSBlockOperation, and start.
- (void)testNSBlockOperation {
// Put the task in the block
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"NSBlockOperation contains tasks that are not enqueued ========%@"[NSThread currentThread]);
}];
[blockOperation start];
}
Copy the code
The main thread is executed and no new thread is started. Similarly, NSBlockOperation can be multithreaded in conjunction with the NSOperationQueue.
NSBlockOperationContains tasks that are not enqueued ========<NSThread: 0x6000000783c0>{number = 1, name = main}
Copy the code
But NSBlockOperation has a method, addExecutionBlock:, that you can use to make NSBlockOperation multithreaded.
- (void)testNSBlockOperationExecution {
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"NSBlockOperation using addExecutionBlock main task ========%@"[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation adds task 1========%@"[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation adds task 2========%@"[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation adds tasks using addExecutionBlock method 3========%@"[NSThread currentThread]);
}];
[blockOperation start];
}
Copy the code
When NSBlockOperation is created, the tasks in the block are executed in the main thread, while the tasks added with addExecutionBlock are executed in the child thread.
NSBlockOperationUsing addExecutionBlock = = = = = = = ="NSThread: 0x60800006ccc0>{number = 1, name = main} addExecutionBlock method adds a task1= = = = = = = ="NSThread: 0x60800007ec00>{number = 3, name = (null)} addExecutionBlock method adds a task3= = = = = = = ="NSThread: 0x6000002636c0>{number = 5, name = (null)} addExecutionBlock method adds a task2= = = = = = = ="NSThread: 0x60800007e800>{number = 4, name = (null)}
Copy the code
- Apply subclasses derived from NSOperation
First we define a class that inherits from NSOperation, then override its main method, and then we can use this subclass to perform related operations.
/*******************"WHOperation.h"*************************/
#import <Foundation/Foundation.h>
@interface WHOperation : NSOperation
@end
/*******************"WHOperation.m"*************************/
#import "WHOperation.h"
@implementation WHOperation
- (void)main {
for (int i = 0; i < 3; i++) {
NSLog(@" Subclass of NSOperation WHOperation======%@"[NSThreadcurrentThread]); }}@end
/ * * * * * * * * * * * * * * * * * back to the main controller using WHOperation * * * * * * * * * * * * * * * * * * * * * * /
- (void)testWHOperation {
WHOperation *operation = [[WHOperation alloc] init];
[operation start];
}
Copy the code
The result is as follows, again executed on the main thread.
Subclass of SOperation WHOperation======<NSThread: 0x608000066780>{number = 1, name = main}
NSOperationA subclass of WHOperation = = = = = ="NSThread: 0x608000066780>{number = 1, name = main}
NSOperationA subclass of WHOperation = = = = = ="NSThread: 0x608000066780>{number = 1, name = main}
Copy the code
So NSOperation needs to work with the NSOperationQueue to implement multithreading. So let’s talk about the NSOperationQueue.
3: queue NSOperationQueue
There are only two types of NSOperationQueue: primary queue and other queues. Other queues contain both serial and concurrent queues.
The main queue is created as follows, where tasks are executed on the main thread.
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
Copy the code
Other queues (non-main queues) are created as follows, with tasks added to ‘non-queues’ being concurrent by default, with multi-threading enabled.
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
Copy the code
Note:
- Non-primary queue columns (other queues) can be serial or parallel.
- Queue NSOperationQueue has a parameter called maximum concurrency: maxConcurrentOperationCount.
- MaxConcurrentOperationCount defaults to 1, direct concurrent execution, so join the default task of ‘the queue is concurrent, open multiple threads.
- When the maxConcurrentOperationCount for 1, said don’t open threads, or serial.
- When the maxConcurrentOperationCount is greater than 1, concurrent execution.
- 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
Add the task to the queue using the – (void)addOperation:(NSOperation *)op method.
- (void)testOperationQueue {
// Create a queue, concurrency by default
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// Create operation, NSInvocationOperation
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationAddOperation) object:nil];
// Create operation, NSBlockOperation
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"addOperation adds tasks to queue ======%@"[NSThreadcurrentThread]); }}]; [queue addOperation:invocationOperation]; [queue addOperation:blockOperation]; } - (void)invocationOperationAddOperation {
NSLog("InvocationOperation ===aaddOperation Adds the task to the queue ====%@"[NSThread currentThread]);
}
Copy the code
Run the following result, you can see that the task is executed in the child thread, start a new thread!
InvocationOperation ===addOperation Adds tasks to queue ====<NSThread: 0x60800026ed00>{number = 4, name = (null)} addOperation Add task to queue ======<NSThread: 0x60800026e640>{number = 3, name = (null)} addOperation Add task to queue ======<NSThread: 0x60800026e640>{number = 3, name = (null)} addOperation Add task to queue ======<NSThread: 0x60800026e640>{number = 3, name = (null)}
Copy the code
- AddOperationWithBlock adds the task to the queue
This is a much easier way to add tasks to a queue by writing them directly in a block.
- (void)testAddOperationWithBlock {
// Create a queue, concurrency by default
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// Add operations to the queue
[queue addOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"addOperationWithBlock adds the task to the queue ======%@"[NSThreadcurrentThread]); }}]; }Copy the code
The result of the run is that the task is indeed executed in a child thread.
AddOperationWithBlock adds the task to the queue ======<NSThread: 0x6000000752c0>{number = 3, name = (null)} addOperationWithBlock adds task to queue ======<NSThread: 0x6000000752c0>{number = 3, name = (null)} addOperationWithBlock adds task to queue ======<NSThread: 0x6000000752c0>{number = 3, name = (null)}
Copy the code
- Use the maximum number of concurrent implementation of serial
Have said above, can use the 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 {
// Create a queue, concurrency by default
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// The maximum concurrency is 1, serial
queue.maxConcurrentOperationCount = 1;
// The maximum number of concurrent requests is 2
// queue.maxConcurrentOperationCount = 2;
// Add operations to the queue
[queue addOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog("AddOperationWithBlock adds the task to the queue 1======%@"[NSThreadcurrentThread]); }}];// Add operations to the queue
[queue addOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"addOperationWithBlock adds task to queue 2======%@"[NSThreadcurrentThread]); }}];// Add operations to the queue
[queue addOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"addOperationWithBlock adds task to queue 3======%@"[NSThreadcurrentThread]); }}]; }Copy the code
The running result is as follows: when the maximum number of concurrent tasks is 1, although the thread is started, the tasks are executed sequentially, so serial is implemented. You can try to change the maximum number of concurrent tasks above to 2 and see that the task becomes concurrent.
AddOperationWithBlock adds the task to the queue1= = = = = ="NSThread: 0x608000068980>{number = 3, name = (null)} addOperationWithBlock adds the task to the queue1= = = = = ="NSThread: 0x608000068980>{number = 3, name = (null)} addOperationWithBlock adds the task to the queue1= = = = = ="NSThread: 0x608000068980>{number = 3, name = (null)} addOperationWithBlock adds the task to the queue2= = = = = ="NSThread: 0x608000068980>{number = 3, name = (null)} addOperationWithBlock adds the task to the queue2= = = = = ="NSThread: 0x608000068980>{number = 3, name = (null)} addOperationWithBlock adds the task to the queue2= = = = = ="NSThread: 0x608000068980>{number = 3, name = (null)} addOperationWithBlock adds the task to the queue3= = = = = ="NSThread: 0x608000068980>{number = 3, name = (null)} addOperationWithBlock adds the task to the queue3= = = = = ="NSThread: 0x608000068980>{number = 3, name = (null)} addOperationWithBlock adds the task to the queue3= = = = = ="NSThread: 0x608000068980>{number = 3, name = (null)}
Copy the code
5: 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
// Pause the queue
[queue setSuspended:YES];
Copy the code
- Determines whether the queue is paused
- (BOOL)isSuspended
Copy the code
Pause and cancel do not cancel the current action immediately, but wait for the current action to complete the execution of no new action.
No.6: Operation dependency of NSOperation
NSOperation has a very useful method, which is operation dependency. You can read it literally: an operation (Operation2) depends on another operation (Operation1), and operation2 can only be executed when Operation1 is complete.
- (void)testAddDependency {
// Concurrent queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
/ / 1
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"operation1======%@"[NSThreadcurrentThread]); }}];/ / 2
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"****operation2 depends on Operation1. Only when Operation1 completes will operation2 execute ****");
for (int i = 0; i < 3; i++) {
NSLog(@"operation2======%@"[NSThreadcurrentThread]); }}];Make operation 2 dependent on operation 1
[operation2 addDependency:operation1];
// Queue the operation
[queue addOperation:operation1];
[queue addOperation:operation2];
}
Copy the code
The result of the run is that operation 2 is always executed after operation 1, successfully verifying the above statement.
operation1======<NSThread: 0x60800026dec0>{number = 3, name = (null)}
operation1======<NSThread: 0x60800026dec0>{number = 3, name = (null)}
operation1======<NSThread: 0x60800026dec0>{number = 3, name = (null)} **** Operation2 depends on Operation1. Operation2 is executed only when Operation1 is complete. ****operation2 ======<NSThread: 0x60800026dc80>{number = 4, name = (null)}
operation2======<NSThread: 0x60800026dc80>{number = 4, name = (null)}
operation2======<NSThread: 0x60800026dc80>{number = 4, name = (null)}
Copy the code