Multithreading for iOS development (5)-Pthreads

Collected Works

Multithreading for iOS development (1)
-Overview of multithreading for iOS development (2)-Multithreading for Thread
iOS development (3)-Multithreading for GCD
iOS development (4)
-Operation Multithreading for iOS development ( 5)-Multithreading of Pthreads
iOS development (6)-Thread safety and various locks

API introduction

1. Create

/**
 thread: 线程ID
 attr: 线程属性, 一般为NULL
 start_routine: 新线程入口函数
 arg: 入口函数start_routine的参数 (例如使用C++编程时的this指针)
 返回值int: 创建成功返回0, 失败返回错误码
 */
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(start_routine)(void*), void *arg);

2. Termination

There are several methods for thread termination:

  1. Return from the main function (after calling return);
  2. Call pthread_exit() by yourself;
  3. Other threads call pthread_cancel();
  4. Calling exit() anywhere in the process causes the process to end.

3. Separation and merging of threads (resource management)

There are two states of pthread:

  • joinable state (default state)
  • unjoinable state

When we create a thread, there is a pthread_attr_t *attr attribute in pthread_create(), which has a separate attribute (in addition to binding attributes, scheduling attributes, etc.), we generally set this attribute to null, that is, use the default attribute. The state of pthread is the joinable state. joinable is mergeable as the name implies, and unjoinable is not mergeable. The
so-called merge refers to waiting for the end of thread B in thread A, and it is said that B is merged with A.

When can threads be merged and when can they not be merged?
This has to do with the resource management of threads. Threads belong to system resources (such as memory resources similar to them). If they are created, they must be destroyed and recycled, otherwise there will be resource leakage. (Like a similar memory leak).

There are two ways to recycle resources:

  • System automatic recycling
  • Programmers write code to recycle actively

In the default state (joinable), resources are actively recycled by the programmer (using the pthread_join() method); while the unjoinable state is automatically recycled by the system, and programmers cannot manually merge (recycle resources).

How to switch to the unjoinable state and let the system automatically reclaim resources?
You can set the detach attribute when creating; or call the pthread_detach() method after the thread is created with the default attribute (passing null), and then it becomes unjoinable by the system The resources are automatically recovered. It can also be said that separation is the management of resources to the system to handle.

In summary, the separation and merging of threads have the following methods (API):

// 分离线程, 由系统自动管理资源, 之后不需再调用pthread_join()
int pthread_detach(pthread_t);

// 合并线程, 即手动管理(回收)资源
int pthread_join(pthread_t , void * _Nullable * _Nullable)

By the way, also note that pthread_join() will block the current thread and wait until the thread to be merged ends before continuing to execute.

Example 1

#import <pthread.h>

pthread_t   m_threadID;  // 线程ID (全局变量)


- (void)viewDidLoad {
    
    
    [super viewDidLoad];

    // 创建线程
    [self createPthread];
    sleep(1);
    // 手动取消线程
    [self cancelThread];
}


// 创建线程
- (void)createPthread {
    
    
    
    /**
     参数1: 线程ID
     参数2: 线程属性, 一般为NULL
     参数3: 新线程入口函数
     参数4: 入口函数的参数
     */
    int ret = pthread_create(&m_threadID, NULL, myThread, NULL);
    if (ret != 0) {
    
    
        NSLog(@"!!! 创建失败 err:%d", ret);
        return;
    }
    
    // 分离线程 (自动管理资源, 后面不需调用pthread_join()来回收)
    pthread_detach(m_threadID);
}


// 线程入口函数
void *myThread(void *param)
{
    
    
    NSLog(@"1, %s, thread:%@", __func__, [NSThread currentThread]);
    
    sleep(3);
    
    NSLog(@"2, %s, thread:%@", __func__, [NSThread currentThread]);
    
//    pthread_exit(NULL);   // 这里作用与return差不多
    return NULL;
}


// 取消线程
- (void)cancelThread {
    
    
    
    pthread_cancel(m_threadID);     // 取消线程
//    pthread_join(m_threadID, NULL); // 如已分离线程则不需此步骤
}

In this example, since the main thread immediately cancels the child thread 1 second after creating the child thread, only log1 will be printed and log2 will not be executed.

log:
1, myThread, thread:<NSThread: 0x600000a01a80>{
    
    number = 6, name = (null)}

Thread synchronization

There are many thread synchronization mechanisms: mutexes, events, semaphores, etc.
Here we only discuss the simplest: mutexes.
Three lock operations:

  • 加锁pthread_mutex_lock()
  • Unlock pthread_mutex_unlock()
  • Try to lock pthread_mutex_trylock()

If a thread is successfully locked, it will enjoy the thread resources in the lock alone, and other threads cannot access until it is unlocked. But if the acquisition fails (for example, other threads have already acquired it), then the thread that failed to acquire will be suspended , Can not resume operation until the lock is released (unlocked). If we don’t want to wait, we can use try lock pthread_mutex_trylock(). The only difference from pthread_mutex_lock() is that the thread will not be suspended if the lock fails. It is up to us to decide whether the thread continues to wait or do other tasks.

Example 2

pthread_mutex_t m_mutex;    // 互斥锁
int m_count;                // 测试数

// 同步两个线程
- (void)syncPthread {
    
    
    
    pthread_mutex_init(&m_mutex, NULL);             // 创建锁
    
    pthread_t pth1,pth2;
    pthread_create(&pth1, NULL, thread1, NULL);     // 创建线程1
    pthread_create(&pth2, NULL, thread2, NULL);     // 创建线程2
    pthread_join(pth1, NULL);                       // 等待回收线程1
    pthread_join(pth2, NULL);                       // 等待回收线程2
        
    pthread_mutex_destroy(&m_mutex);                // 销毁锁
}


void *thread1(void *arg)
{
    
    
    for (int i=0; i<10; i++)
    {
    
    
        pthread_mutex_lock(&m_mutex);
        NSLog(@"%s, count=%d", __func__, m_count);
        m_count++;
        pthread_mutex_unlock(&m_mutex);
        sleep(1);
    }
    NSLog(@"%s, end", __func__);
        
    return NULL;
}


void* thread2(void *arg)
{
    
    
    for (int i=0; i<10; i++)
    {
    
    
        pthread_mutex_lock(&m_mutex);
        NSLog(@"%s, count=%d", __func__, m_count);
        m_count++;
        pthread_mutex_unlock(&m_mutex);
        sleep(2);
    }
    NSLog(@"%s, end", __func__);
        
    return NULL;
}

It can be seen that although the two threads execute alternately, since the mutex does not compete for resources, the m_count is +1 in order.

log:
thread2, count=0
thread1, count=1
thread1, count=2
thread1, count=3
thread2, count=4
thread1, count=5
thread1, count=6
thread2, count=7
thread1, count=8
thread1, count=9
thread2, count=10
thread1, count=11
thread1, count=12
thread2, count=13
thread1, count=14
thread1, end
thread2, count=15
thread2, count=16
thread2, count=17
thread2, count=18
thread2, count=19
thread2, end

demo
https://github.com/LittleLittleKang/KKThreadsDemo

Guess you like

Origin blog.csdn.net/u012078168/article/details/107259707