iOS 多线程之 GCD 的基本使用

什么是GCD

全称Grand Central Dispatch 中暑调度器 纯C语言 提供了很多强大的函数

GCD 的优势

GCD是苹果公司为多核的并行运算提出的解决方案

GCD会自动利用更多的CPU内核(比如 双核 四核)

GCD会自动管理线程的生命周期 (创建线程 调度任务 销毁线程)

程序员只需要告诉GCD想要执行什么任务 不需要编写任何线程管理的代码

GCD的核心概念任务和队列

任务: 需要执行的操作

队列:用来存放任务 调度任务 安排任务在哪个线程中执行

GCD的使用步骤

1 定制任务 确定想要做的事情

2 将任务添加到队列中 GCD会自动的将队列中的任务取出 放到对应的线程中执行 任务的取出遵循队列的FIFO原则 先进先出 后今后出

GCD中执行任务的常用方式

用同步的方式来执行任务

dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);

用异步的方式执行任务

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

同步和异步的区别

同步:只能在当前线程中执行任务 不具备开启线程的能力

异步:  可以在新的线程中执行任务 具备开启线程的能力

GCD中队列的类型

队列的作用 存放任务 并安排任务在相应的线程中执行

并发队列 可以让多个任务同时执行任务 (自动开启多个线程同时执行任务)

              并发功能只有在异步(dispatch_async)函数下才有效

串行队列 让任务一个接着一个执行(一个任务执行完毕后 在执行下一个任务)

所以

同步和异步主要影响 能不能开启新线程

同步 只是在当前线程中执行任务 不具备开启新线程的能力

异步 可以在新的线程中执行任务 具备开启新线程的能力

并行和串行 影响的是 任务的执行方式

并发 允许多个任务同时执行

串行  一个任务执行完毕后 再执行下一个任务

GCD 的基本使用

异步函数 + 并发队列  如果队列中有多个任务会开启多条的线程 任务的执行没有顺序(并发执行或者叫异步执行)

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self asyncConcurrent];
}
//异步函数 + 并发队列 会开启多条线程 队列中的任务异步执行 没有顺序
- (void)asyncConcurrent {
    //1 创建队列
    /*
     *第一个参数 C语言的字符串 标签
     *第二个参数 是队列的类型DISPATCH_QUEUE_CONCURRENT 并发队列 DISPATCH_QUEUE_SERIAL串行队列
     */
    dispatch_queue_t queue = dispatch_queue_create("com.tian.download", DISPATCH_QUEUE_CONCURRENT);
    //第二步 封装任务->添加任务到队列中 在Block中封装任务
    dispatch_async(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download3 --- %@",[NSThread currentThread]);
    });
}

2018-03-06 22:14:48.892107+0800 GCDDemo[941:42730] download3 --- <NSThread: 0x60400026fe40>{number = 5, name = (null)}
2018-03-06 22:14:48.892107+0800 GCDDemo[941:42518] download1 --- <NSThread: 0x60800026bbc0>{number = 3, name = (null)}
2018-03-06 22:14:48.892107+0800 GCDDemo[941:42728] download2 --- <NSThread: 0x60800026ba40>{number = 4, name = (null)}

异步函数 + 串行队列 即使是多个任务 也只会开启一条线程(因为串行队列 任务一个接一个执行 不需要开启多个此线程) 任务的执行是顺序的(串行执行)

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
    [self asyncSerial];
}
//异步函数 + 串行队列  即使是多个任务 也只会开启一条线程 任务的执行是顺序的(串行执行)
- (void)asyncSerial {
    //1 创建队列
    dispatch_queue_t queue = dispatch_queue_create("tian", DISPATCH_QUEUE_SERIAL);
    
    //第二步 封装任务->添加任务到队列中 在Block中封装任务
    dispatch_async(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download3 --- %@",[NSThread currentThread]);
    });
    
}

2018-03-06 22:26:58.083762+0800 GCDDemo[979:50891] download1 --- <NSThread: 0x60800006c340>{number = 3, name = (null)}
2018-03-06 22:26:58.084028+0800 GCDDemo[979:50891] download2 --- <NSThread: 0x60800006c340>{number = 3, name = (null)}
2018-03-06 22:26:58.084211+0800 GCDDemo[979:50891] download3 --- <NSThread: 0x60800006c340>{number = 3, name = (null)}

同步函数 + 并发队列  即使是多个任务也不会开启线程 所以任务是在一个线程中一个接一个的完成的 (串行执行) 按顺序完成

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
//    [self asyncSerial];
    [self syncConcurrent];
}

//同步函数 + 并发队列 不会开线程 只会在当前线程中执行 任务是串行执行的(按顺序执行的)
- (void)syncConcurrent {
    dispatch_queue_t queue = dispatch_queue_create("com.tian.download", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"download3 --- %@",[NSThread currentThread]);
    });
}
2018-03-06 22:44:17.280207+0800 GCDDemo[1010:58942] download1 --- <NSThread: 0x60c00006f4c0>{number = 1, name = main}
2018-03-06 22:44:17.280432+0800 GCDDemo[1010:58942] download2 --- <NSThread: 0x60c00006f4c0>{number = 1, name = main}
2018-03-06 22:44:17.280593+0800 GCDDemo[1010:58942] download3 --- <NSThread: 0x60c00006f4c0>{number = 1, name = main}

同步函数 + 串行队列  不会开启线程 任务是串行执行

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
//    [self asyncSerial];
//    [self syncConcurrent];
    [self syncSerial];
}
//同步函数 + 串行队列
- (void)syncSerial {
    //1 创建队列
    dispatch_queue_t queue = dispatch_queue_create("tian", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"download3 --- %@",[NSThread currentThread]);
    });
}
2018-03-06 22:54:41.556594+0800 GCDDemo[1041:63951] download1 --- <NSThread: 0x60c0002601c0>{number = 1, name = main}
2018-03-06 22:54:41.556861+0800 GCDDemo[1041:63951] download2 --- <NSThread: 0x60c0002601c0>{number = 1, name = main}
2018-03-06 22:54:41.557016+0800 GCDDemo[1041:63951] download3 --- <NSThread: 0x60c0002601c0>{number = 1, name = main}

默认的并发队列

//全局并发队列
    //第一个参数 优先级 第二个参数 预留参数 传0
    /*#define DISPATCH_QUEUE_PRIORITY_HIGH 2
     *#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
     *#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
     *#define DISPATCH_QUEUE_PRIORITY_BACKGROUND 优先级最低 
     */

    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //和创建的并发队列 大部分情况下是一样的 但是也有一些区别
    //一个是直接创建的 系统本身不存在的 一个是拿系统封装好的 系统中本身是存在的

并不是说有多少个任务就开启多少个线程 开启多少个线程是系统控制的。

GCD主队列的使用

获取主队列

dispatch_get_main_queue()

异步函数 + 主队列

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
//    [self asyncSerial];
//    [self syncConcurrent];
//    [self syncSerial];
    [self asyncMain];
}
//异步函数 + 主队列
//凡是放在主队列里面的任务都要在主线程里面执行
//所以没有必须要开线程
//不会开线程 任务串行执行
- (void)asyncMain {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--1-- 主队列  %@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--2-- 主队列  %@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--3-- 主队列  %@",[NSThread currentThread]);
    });
}

同步函数 + 主队列 错误代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
//    [self asyncSerial];
//    [self syncConcurrent];
//    [self syncSerial];
    [self asyncMain];
}
//同步函数 + 主队列 产生死锁
/*
 *主队列调度主线程执行任务 在将封装的任务添加到主队列等待调度的时候 主队列正在执行任务(串行执行 一个任务结束才能执行另一个任务) 所以封装的任务永远等不到机会调度完成
 *如果主队列发现当前主线程有任务在执行 那么主队列会暂停调度队列中的任务 直到主线处于空闲为止
 */
- (void)syncMain {
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--1-- 主队列  %@",[NSThread currentThread]);
    });
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--2-- 主队列  %@",[NSThread currentThread]);
    });
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--3-- 主队列  %@",[NSThread currentThread]);
    });
}

但是如果在子线程中执行上述错误代码 是不会产生死锁的 因为 syncMain这个任务是在子线程中 并没有占有主线程。

使用sync函数在当前串行队列(包含主队列)里 添加任务 会导致线程阻塞

 GCD线程间的通信

- (void)downLoadImageFormService {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSURL *urlStr = [NSURL URLWithString:[NSString stringWithFormat:@"*********"]];
        NSData *data = [NSData dataWithContentsOfURL:urlStr];
        UIImage *image = [UIImage imageWithData:data];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
}

GCD常用函数

延迟函数 和 一次性函数

//1 延迟函数
- (void)delay {
    NSLog(@"start");
    //延迟执行的第一种方式
    [self performSelector:@selector(task) withObject:nil afterDelay:2.0];
    //第二种方式
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:NO];
    //第三种方式
    /*
     *第一个参数 DISPATCH_TIME_NOW 从现在开始
     *第二个参数 要延迟的时间 2.0 * NSEC_PER_SEC = 2 * 10 的九次方 GCD的时间单位是纳秒 转换成秒
     *第三个参数 队列 主线程队列 在主线程执行 并发队列 在子线程里面执行
     */
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self task];
    });
}
- (void)task {
    NSLog(@"延迟任务");
}
//2 一次性代码 整个应用程序只会执行一次的代码 常用在单例模式里面
- (void)once {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"整个程序我只会执行一次哦");
    });
}

栅栏函数 保证队列里任务的执行顺序 不能使用全局并发队列 否则无效

//栅栏函数 适用于需要控制队列里面任务的执行顺序
- (void)barrier {
    //栅栏函数 不能使用全局并发队列 否则无效
    dispatch_queue_t queue = dispatch_queue_create("cdownload", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"任务1 %@",[NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"栅栏 保证顺序执行");
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2 %@",[NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"栅栏 保证任务3最后执行");
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3 %@",[NSThread currentThread]);
    });
}

快速迭代函数

//快速迭代函数 就是遍历函数 比较快是因为会开子线程 for循环是在主线程中完成的 这就造成了遍历是无序的
- (void)apply {
    /*
     *第一个参数 要遍历的次数
     *第二个参数 队列 (只能传并发队列 主队列会死锁 串行队列 没效果)
     *size_t index 索引值
     */
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
        NSLog(@"%zu,%@",index,[NSThread currentThread]);
    });
}

GCD的队列组的使用 group

//队列组 可以监听队列组中任务的完成情况
- (void)group {
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //创建队列组
    dispatch_group_t group = dispatch_group_create();
    //使用异步函数封装任务
    //封装任务-》添加到队列-》会监听任务的执行情况通知group
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务1 %@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务2 %@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务3 %@",[NSThread currentThread]);
    });
    //当队列组中所有的任务都执行完毕的时候 会进入到下面的方法
    //但是这个方法也是异步的 不是阻塞的
    dispatch_group_notify(group, queue, ^{
        NSLog(@"组中的任务都执行完了");
    });
}

- (void)group1 {//以前的写法
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //创建队列组
    dispatch_group_t group = dispatch_group_create();
    //在该方法后面的异步任务 会被纳入到队列组的监听范围内
    //enter 和 leave 必须要配对使用
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
        //当任务执行完毕 退出队列组
        dispatch_group_leave(group);
    });
    dispatch_async(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务已经全部完成");
    });
//    //等到队列组中所有的任务都执行完毕之后才能执行 本身是阻塞的 这行代码不执行 下面的代码也不会执行 与dispatch_group_notify效果等同
//    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//    NSLog(@"任务已经被执行完毕");
}
- (void)demo {
    //下载图片 下载图片 合并图片
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group,queue, ^{
        NSURL *urlStr = [NSURL URLWithString:[NSString stringWithFormat:@"*********"]];
        NSData *data = [NSData dataWithContentsOfURL:urlStr];
        self.image1 = [UIImage imageWithData:data];
    });
    dispatch_group_async(group,queue, ^{
        NSURL *urlStr = [NSURL URLWithString:[NSString stringWithFormat:@"*********"]];
        NSData *data = [NSData dataWithContentsOfURL:urlStr];
        self.image2 = [UIImage imageWithData:data];
    });
    dispatch_group_notify(group, queue, ^{
        //合并图片
        //创建图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(200, 200));
        //画图1
        [self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
        //画图2
        [self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
        //根据上下文得到一张图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        //关闭上下文
        UIGraphicsEndImageContext();
        //更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
    
}

多线程的安全隐患

资源共享: 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。

比如过个线程访问同一个对象 同一个变量 同一个文件

卖票案例:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.ticketsCount = 30;
    [self saleTickets];
}

- (void) saleTicket {
    NSInteger oldTicketsCount = self.ticketsCount;
    sleep(.2);
    oldTicketsCount--;
    self.ticketsCount = oldTicketsCount;
    NSLog(@"还剩%ld张票 %@",(long)oldTicketsCount,[NSThread currentThread]);
}

- (void)saleTickets {
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [self saleTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [self saleTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [self saleTicket];
        }
    });
}
2020-12-07 16:57:33.122710+0800 线程[8095:185490] 还剩29张票 <NSThread: 0x600003b70240>{number = 5, name = (null)}
2020-12-07 16:57:33.122710+0800 线程[8095:185489] 还剩29张票 <NSThread: 0x600003b74f00>{number = 6, name = (null)}
2020-12-07 16:57:33.122714+0800 线程[8095:185495] 还剩29张票 <NSThread: 0x600003b7af00>{number = 7, name = (null)}

可以看到 出了很大的问题 三个线程访问了 同一个变量 卖了三张票 还剩下29张票。因为他们同时拿到了30进行售卖,但其实每个线程第一次应该拿到 30 29 28 的。我们应该保证一个操作完成后,再让另一个线程进入完成操作。

解决方案:使用线程同步技术(同步 就是协同步调 按预定的先后顺序进行运行) 常见的线程同步技术 就是加锁。

OSSpinLock

自旋锁 等待锁的线程回处于忙等(busy-wait)状态 一直占用着CPU资源

注意: 使用这种加锁技术 这个锁必须是同一把锁,也就是声明一个全局的锁使用。

同步原理:如果一个线程访问了加锁的代码块,先判断一个这个锁是否被别人加锁了 如果加锁了 就等待这个锁被解开(等待的过程是忙等 也就是说不是线程休眠 相当于在执行一个死循环) 然后自己再给这把锁加锁 执行业务代码。所以必须使用同一把锁。

iOS10 以后不再使用 因为安全性低 而且可能出现优先级反转等问题

线程调度:CPU在极短的时间内轮流调用不同的线程 执行任务   线程的优先级:优先级高的被分配资源比较高执行时间比较长或者可以先执行完这个线程中的任务

使用自旋锁优先级反转问题:

线程1 优先级高

线程2 优先级低

如果线程2先被执行 然后进行了加锁 就会导致线程1处于忙等状态,但是由于线程1的优先级比较高 所以CPU会给线程1分配的时间长 导致线程2的任务也不能执行(没有分配时间) 锁就不能被打开 导致线程2中的任务执行不下去 线程1的任务也不能执行 有点死锁的意思。

@interface LFBaseDemo : NSObject

- (void)moneyTest;
- (void)ticketTest;
- (void)otherTest;

#pragma mark - 暴露给子类去使用
- (void)__saveMoney;
- (void)__drawMoney;
- (void)__saleTicket;
@end

#import "LFBaseDemo.h"

@interface LFBaseDemo()
@property (assign, nonatomic) int money;
@property (assign, nonatomic) int ticketsCount;
@end

@implementation MJBaseDemo

- (void)otherTest {}

/**
 存钱、取钱演示
 */
- (void)moneyTest
{
    self.money = 100;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self __saveMoney];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self __drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)__saveMoney
{
    int oldMoney = self.money;
    sleep(.2);
    oldMoney += 50;
    self.money = oldMoney;
    
    NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
}

/**
 取钱
 */
- (void)__drawMoney
{
    int oldMoney = self.money;
    sleep(.2);
    oldMoney -= 20;
    self.money = oldMoney;
    
    NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
}

/**
 卖1张票
 */
- (void)__saleTicket
{
    int oldTicketsCount = self.ticketsCount;
    sleep(.2);
    oldTicketsCount--;
    self.ticketsCount = oldTicketsCount;
    NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
}

/**
 卖票演示
 */
- (void)ticketTest
{
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
//    for (int i = 0; i < 10; i++) {
//        [[[NSThread alloc] initWithTarget:self selector:@selector(__saleTicket) object:nil] start];
//    }
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self __saleTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self __saleTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self __saleTicket];
        }
    });
}

@end

上面就是需要加锁的代码可以看到是访问了同一个变量 并且做出修改。

使用OSSpinLock 进行加锁

#import "LFBaseDemo.h"

@interface OSSpinLockDemo : LFBaseDemo

@end

#import "OSSpinLockDemo.h"
#import <libkern/OSAtomic.h>

@interface OSSpinLockDemo()
// High-level lock
@property (assign, nonatomic) OSSpinLock moneyLock;
@property (assign, nonatomic) OSSpinLock ticketLock;
@end

@implementation OSSpinLockDemo

- (instancetype)init
{
    if (self = [super init]) {
        self.moneyLock = OS_SPINLOCK_INIT;
        self.ticketLock = OS_SPINLOCK_INIT;
    }
    return self;
}

- (void)__drawMoney
{
    OSSpinLockLock(&_moneyLock);
    
    [super __drawMoney];
    
    OSSpinLockUnlock(&_moneyLock);
}

- (void)__saveMoney
{
    OSSpinLockLock(&_moneyLock);
    
    [super __saveMoney];
    
    OSSpinLockUnlock(&_moneyLock);
}

- (void)__saleTicket
{
    OSSpinLockLock(&_ticketLock);
    
    [super __saleTicket];
    
    OSSpinLockUnlock(&_ticketLock);
}

@end

OS_unfair_lock

os_unfair_lock用于取代不安全的OSSpinLock 从iOS10开始支持

从底层的调用来看 等待os_unfair_lock锁的线程会处于休眠状态,并非忙等(等待的线程一直在执行一段无意义的代码 直到锁被解开) 

需要导入 <os/lock.h>

//初始化锁
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//尝试加锁
os_unfair_lock_trylock(&lock);
//加锁
os_unfair_lock_lock(&lock);
//解锁
os_unfair_lock_unlock(&lock);

代码测试 继承于LFBaseDemo

#import "OSUnfairLockDemo.h"
#import <os/lock.h>

@interface OSUnfairLockDemo()
// Low-level lock
// ll lock
// lll
// Low-level lock的特点等不到锁就休眠
@property (assign, nonatomic) os_unfair_lock moneyLock;
@property (assign, nonatomic) os_unfair_lock ticketLock;
@end

@implementation OSUnfairLockDemo

- (instancetype)init
{
    if (self = [super init]) {
        self.moneyLock = OS_UNFAIR_LOCK_INIT;
        self.ticketLock = OS_UNFAIR_LOCK_INIT;
    }
    return self;
  
}

// 死锁:永远拿不到锁
// 比如说只进行了加锁 却忘记了解锁 别的线程进来 拿不到锁 就会进入休眠状态(不是忙等)
- (void)__saleTicket
{
    os_unfair_lock_lock(&_ticketLock);
    
    [super __saleTicket];
    
    os_unfair_lock_unlock(&_ticketLock);
}

- (void)__saveMoney
{
    os_unfair_lock_lock(&_moneyLock);
    
    [super __saveMoney];
    
    os_unfair_lock_unlock(&_moneyLock);
}

- (void)__drawMoney
{
    os_unfair_lock_lock(&_moneyLock);
    
    [super __drawMoney];
    
    os_unfair_lock_unlock(&_moneyLock);
}

@end

pthread_mutex

mutex叫做互斥锁  等待锁的线程会处于休眠状态

需要导入文件 #import <pthread.h>

下面可以看一下加锁解锁的过程

#import "MutexDemo.h"
#import <pthread.h>

@interface MutexDemo()
@property (assign, nonatomic) pthread_mutex_t ticketMutex;
@property (assign, nonatomic) pthread_mutex_t moneyMutex;
@end

@implementation MutexDemo

- (void)__initMutex:(pthread_mutex_t *)mutex
{
    //静态初始化 只能定义的时候就初始化 不能定义好再进行初始化
    //pthread_mutex_t mutexx = PTHREAD_MUTEX_INITIALIZER;
    //下面的两句是错误的 不属于静态初始化
    //pthread_mutex_t mutex1;
    //mutex1 = PTHREAD_MUTEX_INITIALIZER;
    
    //初始化属性
    //pthread_mutexattr_t attr;
    //pthread_mutexattr_init(&attr);
    //pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    //第二个参数 是锁的属性
    //pthread_mutex_init(mutex, &attr);
    //销毁属性
    //pthread_mutexattr_destroy(&attr);
    
    //上面的代码相当于
    pthread_mutex_init(mutex, NULL);
}

- (instancetype)init
{
    if (self = [super init]) {
        //初始化锁
        [self __initMutex:&_ticketMutex];
        [self __initMutex:&_moneyMutex];
    }
    return self;
}

// 死锁:永远拿不到锁
- (void)__saleTicket
{
    //加锁
    pthread_mutex_lock(&_ticketMutex);
    
    [super __saleTicket];
    
    //解锁
    pthread_mutex_unlock(&_ticketMutex);
}

- (void)__saveMoney
{
    pthread_mutex_lock(&_moneyMutex);
    
    [super __saveMoney];
    
    pthread_mutex_unlock(&_moneyMutex);
}

- (void)__drawMoney
{
    pthread_mutex_lock(&_moneyMutex);
    
    [super __drawMoney];
    
    pthread_mutex_unlock(&_moneyMutex);
}

- (void)dealloc
{
    //不使用的时候 销毁锁
    pthread_mutex_destroy(&_moneyMutex);
    pthread_mutex_destroy(&_ticketMutex);
}

@end

如果你运行一下可以发现是能很好地解决问题的。但是下面的这种情况就不同了。

- (void)otherTest {
    pthread_mutex_lock(&_ticketMutex);
    [self otherTest2];
    pthread_mutex_unlock(&_ticketMutex);
}

- (void)otherTest2 {
    pthread_mutex_lock(&_ticketMutex);
    NSLog(@"%s",__func__);
    pthread_mutex_unlock(&_ticketMutex);
}

或者是这样的

- (void)otherTest {
    pthread_mutex_lock(&_ticketMutex);
    [self otherTest];
    pthread_mutex_unlock(&_ticketMutex);
}

仔细看一下 会发现造成死锁。锁永远都解不开 任务也执行不了。这种情况就需要一种递归锁了。pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

递归锁允许同一个线程对一把锁进行多次加锁 所以依然可以在多线程中进行线程同步。

利用pthread_cond_t 条件可以设置线程之间的任务的依赖关系。比如两个线程,线程2的任务依赖于线程1的任务,但是这两个线程的执行顺序我们是无法控制的。这个取决于CPU的调度顺序。如果先调度了线程2,并且两个任务使用了同一把锁。那么我们怎么保证线程1的任务会先被调用呢。这个我们就可以使用pthread_cond_t

// 初始化条件
pthread_cond_init(&_cond, NULL);
// 等待(线程进入休眠) 条件并同时放开这把锁 让其他线程可以加锁 执行任务
pthread_cond_wait(&_cond, &_mutex);
// 信号 唤醒被休眠的线程 使被休眠的线程可以继续执行任务 并同时给锁加锁
pthread_cond_signal(&_cond);

下面就是一个例子 线程1的任务依赖于线程2 就是第一次数组的删除依赖于数组的添加。

#import "MutexDemo3.h"

#import <pthread.h>

@interface MutexDemo3()
@property (assign, nonatomic) pthread_mutex_t mutex;
//条件
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation MutexDemo3

- (instancetype)init
{
    if (self = [super init]) {
        // 初始化属性
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        // 初始化锁
        pthread_mutex_init(&_mutex, &attr);
        // 销毁属性
        pthread_mutexattr_destroy(&attr);
        
        // 初始化条件
        pthread_cond_init(&_cond, NULL);
        
        self.data = [NSMutableArray array];
    }
    return self;
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}

// 生产者-消费者模式

// 线程1
// 删除数组中的元素
- (void)__remove
{
    pthread_mutex_lock(&_mutex);
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待(线程进入休眠) 条件并同时放开这把锁 让其他线程可以加锁 执行任务
        pthread_cond_wait(&_cond, &_mutex);
    }
    
    [self.data removeLastObject];
    NSLog(@"删除了元素");
    
    pthread_mutex_unlock(&_mutex);
}

// 线程2
// 往数组中添加元素
- (void)__add
{
    pthread_mutex_lock(&_mutex);
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    
    // 信号 唤醒被休眠的线程 使被休眠的线程可以继续执行任务 并同时给锁加锁
    pthread_cond_signal(&_cond);
    // 广播
//    pthread_cond_broadcast(&_cond);
    
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
}

@end

执行顺序为 假设先执行了线程1 那么线程1会先给_mutex加锁 如果数组元素的个数为0 那么调用pthread_cond_wait方法使线程休眠 并打开这把锁。线程2就可以给这把锁加锁执行任务了。等线程2为数组添加了元素 调用pthread_cond_signal方法唤醒线程1 并加锁_mutex 线程1的任务执行。

 NSLock NSRecursiveLock

NSLock 是对pthread_mutex_lock 普通锁的封装

NSRecursiveLock 是对pthread_mutex_lock 互斥锁的封装 API和NSLock一样 用法也一样 但是使用场景 用于有递归调用需要线程同步的情况。

@interface NSLock : NSObject <NSLocking> {

- (BOOL)tryLock;
//在这个时间之前 如果能等到这把锁被放开 就加锁返回YES 如果时间之前这把锁没被放开 就加锁失败 等这个锁的时间线程处于休眠
- (BOOL)lockBeforeDate:(NSDate *)limit; @end @protocol NSLocking - (void)lock; - (void)unlock; @end

使用例子:

#import "NSLockDemo.h"

@interface NSLockDemo()
@property (strong, nonatomic) NSLock *ticketLock;
@property (strong, nonatomic) NSLock *moneyLock;
@end

@implementation NSLockDemo


- (instancetype)init
{
    if (self = [super init]) {
        self.ticketLock = [[NSLock alloc] init];
        self.moneyLock = [[NSLock alloc] init];
    }
    return self;
}

// 死锁:永远拿不到锁
- (void)__saleTicket
{
    [self.ticketLock lock];
    
    [super __saleTicket];
    
    [self.ticketLock unlock];
}

- (void)__saveMoney
{
    [self.moneyLock lock];
    
    [super __saveMoney];
    
    [self.moneyLock unlock];
}

- (void)__drawMoney
{
    [self.moneyLock lock];
    
    [super __drawMoney];
    
    [self.moneyLock unlock];
}

@end

NSCondition

是对pthread_mutex_lock 和 pthread_cond_t(条件 可以实现任务的依赖情况)的封装

 @interface NSCondition : NSObject <NSLocking> {
 - (void)wait; //相当于 pthread_cond_wait() 线程休眠 并解锁
 //在时间之前等待 时间过后 线程自动结束休眠
 - (BOOL)waitUntilDate:(NSDate *)limit;
 - (void)signal; //相当于 pthread_cond_signal() 通知休眠的线程结束休眠 并上锁
 - (void)broadcast; //相当于 pthread_cond_broadcast()
 @end

应用

#import "NSConditionDemo.h"

@interface NSConditionDemo()
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation NSConditionDemo

- (instancetype)init
{
    if (self = [super init]) {
        self.condition = [[NSCondition alloc] init];
        self.data = [NSMutableArray array];
    }
    return self;
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}

// 生产者-消费者模式

// 线程1
// 删除数组中的元素
- (void)__remove
{
    [self.condition lock];
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待
        [self.condition wait];
    }
    
    [self.data removeLastObject];
    NSLog(@"删除了元素");
    
    [self.condition unlock];
}

// 线程2
// 往数组中添加元素
- (void)__add
{
    [self.condition lock];
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    // 信号
    [self.condition signal];
    
    // 广播
//    [self.condition broadcast];
    [self.condition unlock];
    
}

@end

NSConditionLock

NSConditionLock 是对NSCondition的进一步封装 可以设置一些具体的条件值 可以比较轻松的实现线程依赖

@interface NSConditionLock : NSObject <NSLocking> {

 - (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
//条件
@property (readonly) NSInteger condition;
// 当条件符合时 才能加锁 否则进入休眠状态(等待条件符合 进行加锁)
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
//解锁 并且传入一个新的条件 以便下一个线程加锁
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;

@end

可以看到 主要是多了- (instancetype)initWithCondition:(NSInteger)condition;- (void)lockWhenCondition:(NSInteger)condition;- (void)unlockWithCondition:(NSInteger)condition;这三个方法。运用这三个可以轻松的控制执行线程的顺序。默认条件condition 是0

应用

 

#import "NSConditionLockDemo.h"

@interface NSConditionLockDemo()
@property (strong, nonatomic) NSConditionLock *conditionLock;
@end

@implementation NSConditionLockDemo

- (instancetype)init
{
    if (self = [super init]) {
        self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
    }
    return self;
}

- (void)otherTest {
    // 执行顺序 __one __two __three
    [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}

- (void)__one {
    [self.conditionLock lockWhenCondition:1];
    
    NSLog(@"__one");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:2];
}

- (void)__two {
    [self.conditionLock lockWhenCondition:2];
    
    NSLog(@"__two");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:3];
}

- (void)__three {
    [self.conditionLock lockWhenCondition:3];
    
    NSLog(@"__three");
    
    [self.conditionLock unlock];
}

@end

应用

#import "NSConditionDemo.h"

@interface NSConditionDemo()
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation NSConditionDemo

- (instancetype)init
{
    if (self = [super init]) {
        self.condition = [[NSCondition alloc] init];
        self.data = [NSMutableArray array];
    }
    return self;
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}

// 生产者-消费者模式

// 线程1
// 删除数组中的元素
- (void)__remove
{
    [self.condition lock];
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待
        [self.condition wait];
    }
    
    [self.data removeLastObject];
    NSLog(@"删除了元素");
    
    [self.condition unlock];
}

// 线程2
// 往数组中添加元素
- (void)__add
{
    [self.condition lock];
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    // 信号
    [self.condition signal];
    
    // 广播
//    [self.condition broadcast];
    [self.condition unlock];
    
}

@end

Dispatch_semaphore 

信号量 信号量的初始值 可以用来控制线程并发访问的最大数量

信号量的初始值为1 代表同时只允许一条线程访问资源 保证线程同步

- (void)semaphore {
    //信号量的初始值
    int value = 1;
    //初始化信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(value);
    //如果信号量的值<=0,当前线程就会进入休眠等待(直到信号量的值>0为止)
    //如果信号量的值>0 就减1 然后往下执行后面的代码
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    //让信号量的值加1
    dispatch_semaphore_signal(semaphore);
}

控制最大并发线程数量的应用

self.semaphore = dispatch_semaphore_create(5);
- (void)test
{
    // 如果信号量的值 > 0,就让信号量的值减1,然后继续往下执行代码
    // 如果信号量的值 <= 0,就会休眠等待,直到信号量的值变成>0,就让信号量的值减1,然后继续往下执行代码
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    
    sleep(2);
    NSLog(@"test - %@", [NSThread currentThread]);
    
    // 让信号量的值+1
    dispatch_semaphore_signal(self.semaphore);
}

应用于线程同步

#import "SemaphoreDemo.h"

@interface SemaphoreDemo()
@property (strong, nonatomic) dispatch_semaphore_t ticketSemaphore;
@property (strong, nonatomic) dispatch_semaphore_t moneySemaphore;
@end

@implementation SemaphoreDemo

- (instancetype)init
{
    if (self = [super init]) {
        self.ticketSemaphore = dispatch_semaphore_create(1);
        self.moneySemaphore = dispatch_semaphore_create(1);
    }
    return self;
}

- (void)__drawMoney
{
    dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
    
    [super __drawMoney];
    
    dispatch_semaphore_signal(self.moneySemaphore);
}

- (void)__saveMoney
{
    dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
    
    [super __saveMoney];
    
    dispatch_semaphore_signal(self.moneySemaphore);
}

- (void)__saleTicket
{
    dispatch_semaphore_wait(self.ticketSemaphore, DISPATCH_TIME_FOREVER);
    
    [super __saleTicket];
    
    dispatch_semaphore_signal(self.ticketSemaphore);
}

@end

@synchronized关键字

@synchronized 是对pthread_mutex递归锁的封装

@synchronized(obj)内部会生成obj对应的递归锁 然后进行加锁 解锁的操作

应用

#import "SynchronizedDemo.h"

@implementation SynchronizedDemo

- (void)__drawMoney
{
    @synchronized([self class]) {
        [super __drawMoney];
    }
}

- (void)__saveMoney
{
    @synchronized([self class]) { // objc_sync_enter
        [super __saveMoney];
    } // objc_sync_exit
}

- (void)__saleTicket
{
    static NSObject *lock;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        lock = [[NSObject alloc] init];
    });
    
    @synchronized(lock) {
        [super __saleTicket];
    }
}

- (void)otherTest
{
    @synchronized([self class]) {
        NSLog(@"123");
        [self otherTest];
    }
}
@end

系统会拿着你传进的obj 初始化一把锁 并存进一张哈希表里面(obj的地址为key 锁为value)。所以 你传进的obj必须是唯一的。

 

 

 

 

 

 

 

相关面试题

- (void)test {
    NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        NSLog(@"1");
        [self performSelector:@selector(test) withObject:nil afterDelay:.0];
        //[self performSelector:@selector(test) withObject:nil];
        NSLog(@"3");
    });
    //如果是[self performSelector:@selector(test) withObject:nil afterDelay:.0]; 打印结果是 1 3
    //如果是 [self performSelector:@selector(test) withObject:nil]; 打印结果是1 2 3
}

为什么会出现这种结果呢 其实是因为

performSelector:withObject:这个放发底层就是objc_msgSend方法 和 [self test] 效果一样的 所以可以执行 打印结果为1 2 3

performSelector:withObject:afterDelay: 内部是要开启一个定时器的 而定时器工作依赖于RunLoop 子线程中RunLoop是没有开启的 所以不会执行这一句 就会打印1 3

如果这三句代码 是放在主线程中 打印结果为 1 3 2 虽然只延迟了0秒

- (void)test {
    NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"1");
    }];
    [thread start];
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}

上面的代码 会打印1 然后闪退 原因是 在一个已经释放掉的线程里 执行代码  除非你给这个线程进行保活。

如何用GCD实现 异步并发执行 任务1 任务二 等任务1 任务2 执行完毕后 再回到主线程执行任务3?

对于这个 我们可以使用对列组dispatch_group_t

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i ++) {
            NSLog(@"任务1 %@",[NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i ++) {
            NSLog(@"任务2 %@",[NSThread currentThread]);
        }
    });
    //会保证前面group中的任务执行完毕 然后在执行下面的 当然前面的任务是没有顺序的。
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"任务3");
    });
}

你理解的多线程

1.线程是进程的基本单元 一个进程(运行的程序)所有的任务都要在线程中执行 一个进程至少有一条线程(主线程) 否则无法执行任务

2.同一个进程的线程共享本进程的地址空间 进程之间是互相独立的地址空间 一个进程的崩溃不会影响别的进程 但是一个进程中的线程崩溃会导致整个进程崩溃。

3.单核CPU的情况下 CPU同时只能处理一个线程 是通过快速的在不同线程切换来让用户觉得在同时执行多个线程任务的。多核CPU更能发挥多线程的优势。

4.如果开的线程过多 CPU会在多个线程之间来回切换 消耗大量的CPU资源 每个线程调度的次数会被降低 会导致线程的执行效率降低。

5.每个线程都对应一个RunLoop 只有主线程的RunLoop是默认开启的。子线程如果不开启RunLoop 执行完任务会进入休眠状态。

6.iOS中常用的多线程技术有NSThread GCD NSOperation 需要注意的是多个线程访问同一资源 会造成资源抢夺的问题 使用线程同步技术可以避免

7.线程的状态 新建 准备就绪 运行 阻塞 死亡 

新建:实例化线程对象

就绪:向线程对象发送start消息 线程对象就会被加入到可调度的线程池等待被CPU调度

运行:CPU负责调度可调度线程池中线程的执行 线程执行完成之前 状态可能会在就绪和运行之间来回切换  就绪和运行之间的状态切换由CPU负责

阻塞:当满足某个预定的条件时 可以使用休眠或者锁 阻塞线程执行

死亡:正常死亡 线程执行完毕 非正常死亡 当满足某个条件时 在线程内部中止执行。

自旋锁 互斥锁比较

其实在现在的iOS 中已经不必在意这种问题了因为iOS中唯一的自旋锁OSSPinLock已经不被推荐使用了,用来代替他的事os_unfair_lock也是互斥锁.

使用自旋锁比较划算:

1.预计线程等待锁的时间很短

2.加锁的代码(临界区 加锁与解锁之间的代码)经常被调用 但是竞争情况(多线程同时访问)很少发生

3.CPU资源不紧张

使用互斥锁比较划算:

1.预计线程等待锁的时间很长

2.单核处理器

3.临界区有文件的写入写出操作

4.临界区代码复杂或者循环量大 竞争非常激烈

文件多读单写的实现方案

1. 同一时间 只能有一个线程进行写的操作

2.同一时间 允许有多个线程进行读的操作

3.同一时间 不允许既有写的操作 又有读的操作

我们可以使用:

pthread_rwlock(读写锁) 或者 dispatch_barrier_async:异步栅栏调用

pthread_lrwlock 等待锁的线程会进入休眠

    //初始化锁
    pthread_rwlock_t lock;
    pthread_rwlock_init(&lock, NULL);
    //读-加锁
    pthread_rwlock_rdlock(&lock);
    //读-尝试加锁
    pthread_rwlock_tryrdlock(&lock);
    //写加锁
    pthread_rwlock_wrlock(&lock);
    //写尝试加锁
    pthread_rwlock_trywrlock(&lock);
    //解锁
    pthread_rwlock_unlock(&lock);
    //销毁
    pthread_rwlock_destroy(&lock);
#import "ViewController.h"
#import <pthread.h>

@interface ViewController ()
@property (assign, nonatomic) pthread_rwlock_t lock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    pthread_rwlock_init(&_lock, NULL);
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            [self read];
        });
        
        dispatch_async(queue, ^{
            [self write];
        });
    }
}


- (void)read {
    pthread_rwlock_rdlock(&_lock);
    sleep(1);
    NSLog(@"read");
    pthread_rwlock_unlock(&_lock);
}

- (void)write
{
    pthread_rwlock_wrlock(&_lock);
    sleep(1);
    NSLog(@"write");
    pthread_rwlock_unlock(&_lock);
}

- (void)dealloc
{
    pthread_rwlock_destroy(&_lock);
}

@end

dispatch_barrier_async

这个函数传入的并发队列必须是自己创建的 如果传入的是一个串行或是以恶搞全局的并发队列 那么这个函数便等同于dispatch_async函数的效果。

dispatch_barrier_asyncBlock中包含的代码不能与其他代码同时执行

#import "ViewController.h"
#import <pthread.h>

@interface ViewController ()
@property (strong, nonatomic) dispatch_queue_t queue;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
        
    self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(self.queue, ^{
            [self read];
        });
        
        dispatch_async(self.queue, ^{
            [self read];
        });
        
        dispatch_async(self.queue, ^{
            [self read];
        });
        
        dispatch_barrier_async(self.queue, ^{
            [self write];
        });
    }
}


- (void)read {
    sleep(1);
    NSLog(@"read");
}

- (void)write
{
    sleep(1);
    NSLog(@"write");
}

@end

 NSThread的启动流程

 

 

 

  

 

posted @ 2018-03-07 23:30  幻影-2000  阅读(387)  评论(0编辑  收藏  举报