1. Multithreading technology in iOS

  • pthread

Pthread is the same set of multi-threaded apis that can be used on Unix/Linux, etc. Advantages: Cross-platform, portable Disadvantages: Very low level, hard to use (requires solid technology)

  • NSThread

NSThread is a multithreaded API provided by OC in iOS. Advantages: It is object-oriented, easy to use, and can directly manipulate thread objects. Disadvantages: It is not as fast to use as GCD, which requires to declare objects, call object methods, and manage the life cycle of threads

  • GCD

Based on C language, it aims to replace the advantages of NSThread multi-thread technology: the system automatically manages the life cycle, and the use is simple (Block is directly used). The system bottom layer will make full use of the disadvantages of CPU multi-core: the life cycle cannot be manually managed

  • NSOperation

The advantages of gCD-based object-oriented encapsulation are as follows: The gCD-based object-oriented encapsulation allows you to cancel the suspension of a thread, add a thread dependency, and obtain the thread state (such as finished/cancelled). Disadvantages: The gCD-based object-oriented encapsulation is not as fast as the GCD, and objects need to be declared

2. Synchronous, asynchronous, serial, parallel

  • Synchronous and Asynchronous Specifies whether to enable new threads to execute tasks

Asynchronous: A new thread is started and the task is executed on the new thread

  • Serial and parallel mainly describe the way tasks are performed

Serial: Multiple tasks are executed sequentially. Parallel: Multiple tasks are executed concurrently

Adding a task to a serial queue using sync causes a deadlock

3. Multi-threaded synchronization and locking

Multithreading is dangerous. When multiple threads read and write to a resource at the same time, it is easy to cause data confusion. The solution is multi-threaded synchronization, in which multiple threads execute synchronously in sequence. The usual method is to lock

  • OSSpinLock: spin lock

While waiting for the lock, the spin-lock is in the busy state and occupies CPU resources. You can think of it as a while loop

  • os_unfair_lock

Os_unfair_lock is used to replace an unsafe OSSpinLock. The CPU goes to sleep while the underlying CPU is waiting for the lock

  • Pthread_mutexes: a mutex

The wait lock on a mutex is dormant, and using PTHREAD_MUTEX_RECURSIVE type initialization solves the problem of recursive locks

  • NSLock: encapsulates a pthread_mutex

  • NSRecursiveLock: Encapsulation of pthread_mutex recursive locks

  • Dispatch_semaphore: semaphore

When the semaphore value is <=0, the current thread will go into hibernation wait. When the semaphore value is >0, the code after wait() will be executed. The maximum number of concurrent threads can be controlled by initialling the semaphore. Dispatch_semaphore_wait (): makes the semaphore -1 dispatch_semAPhore_signal (): makes the semaphore +1

  • dispatch_queue: GCD

When dispatch_queue is initialized, it can choose to use a serial queue so that tasks in the queue are executed synchronously

  • @synchronized

Encapsulation of pthread_mutex recursive locks

  • Spin locks and mutexes

Spin lock: Waiting for a lock keeps CPU resources occupied or is busy. It consumes CPU resources all the time, so it is suitable for waiting for a short time and CPU resources are not tight. Mutex: Waiting for a lock will go into sleep and wait to wake up. Because sleep awakenings require more resources, they are useful in scenarios where the waiting time for a lock is longer.

  • Dispatch_barrier_async /dispatch_barrier_sync: thread barrier

The thread fence can be used to divide the tasks in front and behind the fence, ensuring that the tasks in front of the fence are completed before the tasks behind the fence are executed

4. Multithreading scenarios

  • Multithreaded ticket selling: multiple threads selling tickets at the same time, to ensure the correct number of tickets
// Set the initial value to 10 and open three threads to sell tickets
- (void)ticketTest {
    self.count = 10;
    self.ticketLock = [[NSLock alloc] init];
    dispatch_queue_t queue = dispatch_get_global_queue(0.0);
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [selfsaleTicket]; }});dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            [selfsaleTicket]; }});dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            [selfsaleTicket]; }}); }// Lock the thread at the beginning, and unlock the thread at the end to ensure synchronization
- (void)saleTicket {
    [self.ticketLock lock];
    
    int tempCount = self.count;
    sleep(3.);
    tempCount--;
    self.count = tempCount;
    
    [self.ticketLock unlock];
    NSLog(@" %d tickets left".self.count);
}

Copy the code
  • There are four tasks A, B, C and D, and the task C and D should be carried out after the completion of A and B
  1. The GCD’s group
// Define group and queue
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);

dispatch_group_async(group, queue, ^{
    NSLog(@"A");
});

dispatch_group_async(group, queue, ^{
    NSLog(@"B");
    sleep(1);  // Assume that task B is delayed completion
});

// Execute task C, D in the notify completed by group
dispatch_group_notify(group, queue, ^{
    NSLog(@"C, D");
});
Copy the code
  1. NSOperation sets the dependency
/ / create the queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *opA = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"A");
}];
NSBlockOperation *opB = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"B");
    sleep(1);  // Assume that task B is delayed completion
}];
NSBlockOperation *opCD = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"C, D");
}];

// set B to depend on A and CD to depend on B
[opB addDependency:opA];
[opCD addDependency:opB];

// Queue the task for execution
[queue addOperation:opA];
[queue addOperation:opB];
[queue addOperation:opCD];
Copy the code

With NSOperation, you must set multiple dependencies at the same time

  1. Dispatch_barrier_async Thread barrier
// Create a queue
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
dispatch_async(queue, ^{
    NSLog(@"A");
});

dispatch_async(queue, ^{
    NSLog(@"B");
    sleep(1); // Assume that task B is delayed completion
});

// Set the thread barrier
dispatch_barrier_async(queue, ^{
    NSLog(@"barrier");
});

dispatch_async(queue, ^{
    NSLog(@"C");
});

dispatch_async(queue, ^{
    NSLog(@"D");
});
Copy the code