iOS开发之多线程(5)—— Pthreads

文集

iOS开发之多线程(1)—— 概述
iOS开发之多线程(2)—— Thread
iOS开发之多线程(3)—— GCD
iOS开发之多线程(4)—— Operation
iOS开发之多线程(5)—— Pthreads
iOS开发之多线程(6)—— 线程安全与各种锁

API介绍

1. 创建

/**
 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. 终止

线程终止有一下几种方法:

  1. 从主函数返回 (调用return之后);
  2. 自己调用pthread_exit();
  3. 其他线程调用pthread_cancel();
  4. 进程中任意地方调用exit()导致进程结束.

3. 线程的分离与合并 (资源管理)

pthread有两种状态:

  • joinable状态 (默认状态)
  • unjoinable状态

我们创建一个线程时, pthread_create()里有个pthread_attr_t *attr属性, 其中有一个分离属性 (另外还有绑定属性, 调度属性等), 我们一般设置这个属性为null, 即使用默认属性, 此时pthread的状态是joinable状态. joinable顾名思义为可合并的, unjoinable则为不可合并的.
所谓合并是指在线程A中等待线程B结束, 称B合并于A.

什么时候线程可合并什么时候又不可合并呢?
这就要和线程的资源管理有关了. 线程属于系统资源 (如与之相似的内存资源), 有创建就要有销毁回收, 不然会有资源泄漏 (如与之相似的内存泄漏).

回收资源有两种方式:

  • 系统自动回收
  • 程序员编写代码主动回收

默认状态(joinable)下, 资源是由程序员主动回收的 (使用pthread_join()方法); 而unjoinable状态是系统自动回收的, 程序员不能手动合并 (回收资源).

怎样切换至unjoinable状态, 让系统自动回收资源呢?
可以在创建的时候设置好分离属性; 或者在使用默认属性 (传null) 创建线程后调用pthread_detach()方法, 这时就变成unjoinable状态由系统自动回收资源了. 也可以说, 分离是将资源的管理交由系统来处理.

综上, 线程的分离与合并有如下方法(API):

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

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

对了, 还要注意, pthread_join()会阻塞当前线程, 等到要合并的线程结束时, 才继续往下执行.

示例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); // 如已分离线程则不需此步骤
}

本例中, 由于主线程在创建子线程1秒后马上取消了子线程, 所以只会打印log1而不会执行到log2.

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

线程同步

线程的同步机制有很多种: 互斥锁, 事件, 信号量等等.
这里我们仅讨论最简单的: 互斥锁.
锁的三种操作:

  • 加锁pthread_mutex_lock()
  • 解锁pthread_mutex_unlock()
  • 尝试加锁pthread_mutex_trylock()

一个线程加锁成功, 就会独自享有锁里面的线程资源, 其他线程无法访问, 直至解锁. 但如果获取失败(比如别的线程已经获取过了), 这时获取失败的线程就会被挂起, 直至锁被释放(解锁)后, 才能恢复运行. 如果我们不想等待, 就可以使用尝试加锁pthread_mutex_trylock(), 和pthread_mutex_lock()唯一不同的是, 尝试加锁失败了线程不会被挂起, 而是由我们决定线程继续等待还是做其他任务.

示例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;
}

可以看到, 虽然两个线程交替执行, 但是由于互斥锁不会争夺资源, m_count都是按顺序+1.

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值