Threads and processes

Thread definition:

  • Threads are the basic unit of execution where all tasks of a process are executed.
  • To perform a task, a process must have at least one thread.
  • By default, the program starts a thread, which is called the main thread or UI thread.

Process definition:

  • A process is a program that is running in the system.
  • Each process is independent of each other, and each process runs in its own dedicated and protected memory.

The relationship and difference between threads and processes:

  1. Address space: Threads of the same process share the address space of the same process, while processes are independent of each other.
  2. Resource ownership: Threads in the same process share the resources of the same process, such as memory, I/O, and CPU, but resources between processes are independent.
  3. A process crash in protected mode does not affect other processes. But one thread crashes and the entire process dies. So multi-process is more robust than multi-thread. Process switchover consumes large resources and is efficient. So when it comes to frequent switching, threads are better than processes. Similarly, if concurrent operations are required and some variables are shared at the same time, only threads can be used, not processes.
  4. Execution process: Each independent process has a program run entry, sequential execution sequence, and program entry. However, threads cannot execute independently and must depend on the application, which provides multiple thread execution control.
  5. Threads are the basic unit of scheduling for processors, but processes are not.

Second, the meaning of threads

Let’s start with some code:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self threadTest];
}

- (void)threadTest{
    for (NSInteger i = 0; i < 10000000; i++) {
        NSLog(@"%d", i); }}Copy the code

When we want to iterate some data in development, we put it in the main thread to do things, and when it runs, we will find that the main interface can’t do anything. This is because the code takes a long time to run, blocking the main thread from running down. To solve this problem, we need to use the concept of multithreading.

We implement multi-threading through NSThread, GDC, NSOperation, etc. Such as:

//1: NSThread
[NSThread detachNewThreadSelector:@selector(threadTest) toTarget:self withObject:nil];
// 2: GCD
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self threadTest];
});
//3: NSOperation
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
    [self threadTest];
}];

Copy the code

The advantages of multithreading are:

  1. Can improve the execution efficiency of the program
  2. Appropriately improve resource utilization (CPU, memory)
  3. Tasks on the thread are automatically destroyed after completion

There are also some disadvantages:

  1. Starting threads takes up a certain amount of memory (512KB per thread by default)
  2. If a large number of threads are enabled, a large amount of memory space will be occupied, reducing the performance of the program
  3. The more threads there are, the more overhead the CPU has on calling threads
  4. The program design is more complex, such as communication between threads, multithreaded data sharing and so on.

Three, the principle of multithreading

Multithreading seems to us to be multiple threads handling tasks. Multithreading the name comes from a single-core CPU, which can only handle one thread at a time, but what about multiple threads? There is a concept called time slice, where the CPU switches back and forth between multiple threads to perform tasks. This time slice is so small that we don’t notice it, giving the illusion that multiple threads are running at the same time.

Multi-threading in the real sense comes from multi-core CPU, which is mainly to speed up the efficiency of execution

3.1 The life cycle of multithreading

For example, NSThread, when we create a thread, our next step is to start it with start. When I execute start, it doesn’t mean that the thread is starting to execute, but that the thread enters a Runnable state, which tells the computer that the thread is ready to run. It waits for the CPU’s resource schedule to execute the thread. The program is forced to exit when it finishes executing, destroying the thread.

When a thread starts running, some kind of blocking prevents the thread from proceeding (such as locking), causing the thread to re-enter the Runnable ready state, waiting to be invoked.

3.2. Thread pools

When the system needs to call a thread, it comes to one place, the thread pool.

The first step is to determine the state of the thread pool in case it becomes full. If the thread pool is not full, it checks whether the work queue is full. If the work queue is full, it checks whether there are idle threads. If the work queue is full, it enters the saturation strategy.

Saturation strategy can be divided into four strategies:

  1. AbortPolicy directly thrown RejectedExecutionExeception exceptions to prevent normal operation of system
  2. CallerRunsPolicy rolls back the task to the caller
  3. DisOldestPolicy Discards the most awaited task
  4. DisCardPolicy Discards the task directly

All four policies implement the RejectedExecutionHandler interface.

Fourth, thread safety

Let’s start with a code:

// 1. Start a ticket thread
NSThread *t1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
t1.name = Ticket and A "@";
[t1 start];

// 2. Start another thread
NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
t2.name = Ticket @ "B";
[t2 start];
Copy the code

If there are two ticket Windows selling tickets at the same time, then there will be A problem, the total number of tickets is the same, it is very likely that B has sold the last ticket when A is selling. This will cause A to access an incorrect number of remaining votes.

To avoid this problem, you need to introduce a lock. For the implementation method:

- (void)saleTickets {
    while(YES) {@synchroized(self) {// ticket operation}}}Copy the code

In this way, we can simply add A lock to the shared resource. When A is performing data operation, B is locked out. Only after A is performing data operation, B can perform data operation.

The lock will be analyzed in detail later.

5. Thread communication

Our normal thread communication is that a thread sends a message to a thread with a value. Here’s an example of end-to-end thread communication, called NSPort.

@interface PortViewController ()<NSMachPortDelegate>
@property (nonatomic, strong) NSPort *myPort;
@property (nonatomic, strong) Person *person;
@end

@implementation PortViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"Port thread communication"; self.view.backgroundColor = [UIColor whiteColor]; Self. MyPort = [NSMachPort]; self. MyPort = [NSMachPort]; Self.myport.delegate = self; self.myport.delegate = self; [[NSRunLoop currentRunLoop] addPort:self.myPortforMode:NSDefaultRunLoopMode];
    
    self.person = [[Person alloc] init];
    [NSThread detachNewThreadSelector:@selector(personLaunchThreadWithPort:)
                             toTarget:self.person
                           withObject:self.myPort];
    
}

#pragma mark - NSMachPortDelegate

- (void)handlePortMessage:(NSPortMessage *)message{
    
    NSLog(@"Message from person :");
    NSArray *messageArr = [message valueForKey:@"components"];
    NSString *dataStr   = [[NSString alloc] initWithData:messageArr.firstObject  encoding:NSUTF8StringEncoding];
    NSLog(@"Incoming message :%@",dataStr);
    NSPort *destinPort = [message valueForKey:@"remotePort"];
    
    if(! destinPort || ! [destinPort isKindOfClass:[NSPort class]]){ NSLog(@"The data sent was wrong.");
        return;
    }
    
    NSData *data = [@"VC received!!!!!!"dataUsingEncoding:NSUTF8StringEncoding]; NSMutableArray *array =[[NSMutableArray alloc]initWithArray:@[data,self.myPort]]; // Very important, if you want to receive information in Person's port, you must add runloop [[NSRunLoop currentRunLoop] addPort:destinPort to the current main threadforMode:NSDefaultRunLoopMode];
    
    BOOL success = [destinPort sendBeforeDate:[NSDate date]
                                        msgid:10010
                                   components:array
                                         from:self.myPort
                                     reserved:0];
    NSLog(@"%d",success);
}


- (void)getAllProperties:(id)somebody{
    
    u_int count = 0;
    objc_property_t *properties = class_copyPropertyList([somebody class], &count);
    for (int i = 0; i < count; i++) {
        const char *propertyName = property_getName(properties[i]);
         NSLog(@"% @",[NSString stringWithUTF8String:propertyName]); } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } / *#pragma mark - Navigation- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { } */ @end @interface Person()<NSMachPortDelegate>  @property (nonatomic, strong) NSPort *vcPort; @property (nonatomic, strong) NSPort *myPort; @end @implementation Person - (void)personLaunchThreadWithPort:(NSPort *)port{ NSLog(@"VC responds to Person"); Autoreleasepool {//1. Save the mainline port self.vcPort = port; //2. Set the name of the child thread [[NSThread currentThread]setName:@"PersonThread"]; Runloop [[NSRunLoop currentRunLoop] run]; // create self port self.myPort = [NSMachPort port]; Self.myport.delegate = self; self.myport.delegate = self; //6. Finish sending the message to the main thread port [self sendPortMessage]; }} /** * sendPortMessage {NSData *data1 = [@"data1" dataUsingEncoding:NSUTF8StringEncoding];
    NSData *data2 = [@"data2"dataUsingEncoding:NSUTF8StringEncoding]; NSMutableArray *array =[[NSMutableArray alloc]initWithArray:@[data1,self.myPort]]; // Send a message to the main thread of the VC // first argument: send time. // msgid message id. // components, send messages with parameters. / / reserved: Number of bytes reserved for the header [self.vcPort sendBeforeDate:[NSDate Date] MSgid :10086 Components :array from: self.myport reserved:0]; }#pragma mark - NSMachPortDelegate
- (void)handlePortMessage:(NSPortMessage *)message{
    NSLog(@"Some information from VC :"); // Fetch data from message's KVC} @endCopy the code