GCD

线程

队列

GCD

GCD 公开有 5 个不同的队列
运⾏在主线程中的主队列:Main Queue,
3个不同优先级: High Priority QueueDefault Priority QueueLow Priority Queue
后台队列:Background Priority Queue

GCD Queues 代码
High Priority Queue DISPATCH_QUEUE_PRIORITY_HIGH
Default Priority Queue DISPATCH_QUEUE_PRIORITY_DEFAULT
Low Priority Queue DISPATCH_QUEUE_PRIORITY_LOW
Background Priority Queue DISPATCH_QUEUE_PRIORITY_BACKGROUND
Custom Queues 代码 描述
Serial Dispatch Queue DISPATCH_QUEUE_SERIAL 串行队列(FIFO)
Concurrent Dispatch Queue DISPATCH_QUEUE_CONCURRENT 并行队列

自定义队列(Custom Queues)

自定义队列在后台执行。

//串行队列
dispatch_queue_t queue = dispatch_queue_create("com.queue", DISPATCH_QUEUE_CONCURRENT);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("com.queue", DISPATCH_QUEUE_SERIAL);

获取主队列

  • 主队列(Main Dispatch Queue) 获取在主线程中执行的队列主队列(Main Queue),因为主线程只有1个,所以Main Queue是串行队列
  • 主线程是在main()函数被调用之前被创建,创建主线程的同时主队列也一起被创建,提交到主队列的blocks将会在主线程执行。
dispatch_queue_t queue = dispatch_get_main_queue();
  • 全局队列(Global Dispatch Queue) 一个所有应用程序都能够使用的并发队列。加入到该队列中的任务不一定会生成线程。因为有线程重用的现象。
//dispatch_get_global_queue(long identifier, unsigned long flags);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAUL, 0);

操作函数

  • 同步操作:不会创建新的线程。在当前线程中按照顺序去执⾏。
void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
  • 异步操作:会创建新的线程。
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
  • 修改 Dispatch Queue 优先级
void
dispatch_set_target_queue(dispatch_object_t object,//需要变更优先级的Dispatch Queue
dispatch_queue_t _Nullable queue //指定要使用的执行优先级相同优先级的Queue。
);
  • 同步操作 + 串行队列
    不创建新的线程,串行执行任务。
 NSLog(@"串行队列和同步函数Began%@", [NSThread currentThread]);
 dispatch_queue_t QUEUE_SERIAL = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
 dispatch_sync(QUEUE_SERIAL, ^{ NSLog(@"任务1%@", [NSThread currentThread]); });
 dispatch_sync(QUEUE_SERIAL, ^{ NSLog(@“任务2%@", [NSThread currentThread]); });
 dispatch_sync(QUEUE_SERIAL, ^{ NSLog(@“任务3%@", [NSThread currentThread]); });
 NSLog(@"串行队列和同步函数End%@", [NSThread currentThread]);
 /************************print********************/
 touchesBegan,<NSThread: 0x7fd452e007e0>{number = 1, name = main}
 串行队列和同步函数Began<NSThread: 0x7fd452e007e0>{number = 1, name = main}
 任务1<NSThread: 0x7fd452e007e0>{number = 1, name = main}
 任务2<NSThread: 0x7fd452e007e0>{number = 1, name = main}
 任务3<NSThread: 0x7fd452e007e0>{number = 1, name = main}
 串行队列和同步函数End<NSThread: 0x7fd452e007e0>{number = 1, name = main}
 touchesEnd,<NSThread: 0x7fd452e007e0>{number = 1, name = main}
  • 同步执行函数 + 并发队列
    不开启新的线程
NSLog(@"并发队列和同步函数Began,%@",[NSThreadcurrentThread]);
dispatch_queue_tGLOBAL_QUEUE=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_sync(GLOBAL_QUEUE,^{NSLog(@"任务1%@",[NSThreadcurrentThread]);});
dispatch_sync(GLOBAL_QUEUE,^{NSLog(@"任务2%@",[NSThreadcurrentThread]);});
dispatch_sync(GLOBAL_QUEUE,^{NSLog(@"任务3%@",[NSThreadcurrentThread]);});
NSLog(@"并发队列和同步函数End,%@",[NSThreadcurrentThread]);
/******************************************************/
并发队列和同步函数Began,<NSThread:0x600000263cc0>{number=1,name=main}
任务1<NSThread:0x600000263cc0>{number=1,name=main}
任务2<NSThread:0x600000263cc0>{number=1,name=main}
任务3<NSThread:0x600000263cc0>{number=1,name=main}
并发队列和同步函数End,<NSThread:0x600000263cc0>{number=1,name=main}
  • 同步执行函数 + 主队列
    卡死主线程、主线程锁死

  • 异步执行函数 + 串行队列
    创建新的线程,串行执行任务

 NSLog(@"串行队列和异步函数Begain,%@", [NSThread currentThread]);
 dispatch_queue_t QUEUE_SERIAL = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
 dispatch_async(QUEUE_SERIAL, ^{NSLog(@"任务1%@", [NSThread currentThread]);});
 dispatch_async(QUEUE_SERIAL, ^{NSLog(@“任务2%@", [NSThread currentThread]);});
 dispatch_async(QUEUE_SERIAL, ^{NSLog(@“任务3%@", [NSThread currentThread]);});
 NSLog(@"串行队列和异步函数End,%@", [NSThread currentThread]); 
 /****************************************/
 串行队列和异步函数Begain,<NSThread: 0x6000000636c0>{number = 1, name = main}
 串行队列和异步函数End,<NSThread: 0x6000000636c0>{number = 1, name = main}
 任务1<NSThread: 0x6000002721c0>{number = 3, name = (null)}
 任务2<NSThread: 0x6000002721c0>{number = 3, name = (null)}
 任务3<NSThread: 0x6000002721c0>{number = 3, name = (null)}
  • 异步执行函数 + 并发队列
    创建线程、并行执行
dispatch_queue_t GLOBAL_QUEUE = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(GLOBAL_QUEUE, ^{NSLog(@"任务1%@", [NSThread currentThread]);});
dispatch_async(GLOBAL_QUEUE, ^{NSLog(@"任务2%@", [NSThread currentThread]);});
dispatch_async(GLOBAL_QUEUE, ^{NSLog(@"任务3%@", [NSThread currentThread]);});
NSLog(@"并发队列和异步函数End,%@", [NSThread currentThread]);
/**************************************************/
并发队列和异步函数Began,<NSThread: 0x60000006b680>{number = 1, name = main}
并发队列和异步函数End,<NSThread: 0x60000006b680>{number = 1, name = main}
任务1<NSThread: 0x600000271e80>{number = 3, name = (null)}
任务2<NSThread: 0x6000002720c0>{number = 4, name = (null)}
任务3<NSThread: 0x60400026f680>{number = 5, name = (null)}
  • 异步执行函数 + 主线程
    不创建新的线程执行任务
NSLog(@"主队列和异步函数Began,%@", [NSThread currentThread]);
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]);});
NSLog(@"主队列和异步函数End,%@", [NSThread currentThread]);
/*************************************/
主队列和异步函数Began,<NSThread: 0x600000077080>{number = 1, name = main}
主队列和异步函数End,<NSThread: 0x600000077080>{number = 1, name = main}
任务1<NSThread: 0x600000077080>{number = 1, name = main}
任务2<NSThread: 0x600000077080>{number = 1, name = main}
任务3<NSThread: 0x600000077080>{number = 1, name = main}
  • 并行处理,主线程刷新
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       /*并行执行的处理*/
        dispatch_async(dispatch_get_main_queue(), ^{
           /*在主线程中进行UI处理操作*/
        });
    });
  • 死锁情况
dispatch_queue_t queue = dispatch_queue_create("com.test", NULL);
    dispatch_async(queue, ^{
        dispatch_sync(queue, ^{
           NSLog(@"任务%@", [NSThread currentThread]);
        });
    });
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"任务%@", [NSThread currentThread]);
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        dispatch_sync(dispatch_get_main_queue(), ^{
           NSLog(@"任务%@", [NSThread currentThread]);
        });
    });

Dispatch Group 组函数

把任务分组,等待这组任务执行完毕,会得到通知。

  • 创建分组:
dispatch_group_t dispatch_group_create(void);
  • 添加任务:将一个任务添加到指定group中
void dispatch_group_async(dispatch_group_t group,
    dispatch_queue_t queue,
    dispatch_block_t block);
  • 任务完成通知
void dispatch_group_notify(dispatch_group_t group,
    dispatch_queue_t queue,
    dispatch_block_t block);

Demo

-(void)demo{
    self.values = [NSMutableArray array];
    group = dispatch_group_create();
    queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
    
    for (NSInteger i = 0; i < 100; i++) {
        dispatch_group_async(group, queue, ^{
          [self.values addObject:@(i)];
        });
    }
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"%@", self.values);
    });

    //进行主界面刷新
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"%@", self.values);
    });
}
// timeout : DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER
long dispatch_group_wait(dispatch_group_t group,
dispatch_time_t timeout //指定为等待的时间(超时), DISPATCH_TIME_FOREVER,意味着永久等待,只要Group处理尚未执行结束,就会一直等待。
)

demo

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{ NSLog(@"group1");});
dispatch_group_async(group, queue, ^{ NSLog(@"group2");});
long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
if (result == 0) {
/*属于Dispatch Group 的全部处理执行结束*/
}else{
/*属于Dispatch Group 的某一个处理还在执行中*/
}

Dispatch Barrier

在访问数据库的时候,使用Serial Dispatch Queue 可以避免数据竞争问题。
写入处理确实不可与其他的写入处理以及包涵读取处理的其他的某些处理并行执行,但是如果读取处理只是与读取处理并行执行,那么多个并行执行就不会发生问题。也就是说,位了高效率的进行访问,读取处理追加到 Concurrent Dispatch Queue 中,写入处理在任一个读取处理没有执行的状态下,追加到 Serial Dispatch Queue 中即可。

void
dispatch_barrier_sync(dispatch_queue_t queue,
        DISPATCH_NOESCAPE dispatch_block_t block);

void
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);

// 指定的Block追加到指定的Queue中,并等待全部处理执行结束
void
dispatch_apply(size_t iterations, dispatch_queue_t queue,
        DISPATCH_NOESCAPE void (^block)(size_t));

// 挂起指定的Dispatch Queue
void
dispatch_suspend(dispatch_object_t object);

// 恢复指定的Dispatch Queue
void
dispatch_resume(dispatch_object_t object);

demo

//在3、4 处理之间执行写入处理,并将写入的内容读取4处理已经之后的处理中。
-(void)barrier{
dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{NSLog(@"读取1");});
dispatch_async(queue, ^{NSLog(@"读取2");});
dispatch_async(queue, ^{NSLog(@"读取3");});
dispatch_barrier_async(queue, ^{NSLog(@"写入");});
dispatch_async(queue, ^{NSLog(@"读取4");});
dispatch_async(queue, ^{NSLog(@"读取5");});
dispatch_async(queue, ^{NSLog(@"读取6");});
dispatch_async(queue, ^{NSLog(@"读取7");});

打印

读取2
读取3
读取1
写入
读取4
读取5
读取6
读取7

使用 Concurrent Dispatch Queue 和 dispatch_barrier_async 结合使用可以实现高效率的数据访问和文件访问。

Dispatch Semaphore 信号量

  • Dispatch Semaphore,是持有计数的信号,该计数是多线程编程中的计数类型型号。
  • 类似于过高速路收费站的栏杆。可以通过时,打开栏杆,不可以通过时,关闭栏杆,在Dispatch Semaphore中,使用计数来实现该功能。计数为0时等待,计数为1或者大于1时,减去1而不等待。
  • 使用效果等价于 Serial Dispatch Queu + dispatch_barrier_async。前者使用粒度比 Dispatch Semaphore 大。
// 创建一个Semaphore并初始化信号的总量
dispatch_semaphore_t
dispatch_semaphore_create(long value);

//等待 Dispatch Semaphore 的计数值达到大于或者等于1
// 当计数值大于等于1,或者在待机中计数值大于等于1时,对该计数进行减法并从 dispatch_semaphore_wait 函数返回。
// timeout 参数 与 dispatch_group_wait 函数相同,由dispatch_time_t 类型值指定等待时间。DISPATCH_TIME_FOREVER 意味着永久等待
long
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

// 发送一个信号,让信号总量加1
long
dispatch_semaphore_signal(dispatch_semaphore_t dsema);

参数表示计数的初始值。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
  • Dispatch Semaphore 线程同步
    正常情况的多线程
- (void)asyncNormal{
    NSLog(@"Start-%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"Task---%@",[NSThread currentThread]);
    });
    NSLog(@"End-%@",[NSThread currentThread]);
}

打印结果:

Start-<NSThread: 0x12f604a40>{number = 1, name = main}
End-<NSThread: 0x12f604a40>{number = 1, name = main}
Task---<NSThread: 0x12f6142a0>{number = 2, name = (null)}

使用信号量进行控制

- (void)asyncSemaphore{
    NSLog(@"Start-%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"Task---%@",[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"End-%@",[NSThread currentThread]);
}

打印结果;

Start-<NSThread: 0x125504920>{number = 1, name = main}
Task---<NSThread: 0x12563e750>{number = 2, name = (null)}
End-<NSThread: 0x125504920>{number = 1, name = main}

异步执行将任务追加到队列之后,不做等待,接着执行dispatch_semaphore_wait方法。
此时 semaphore == 0,当前线程进入等待状态。
异步任务开始执行。
任务执行到dispatch_semaphore_signal之后,总信号量 emaphore == 1
dispatch_semaphore_wait方法使总信号量减1,正在被阻塞的线程(主线程)恢复继续执行。
这样就实现了线程同步,将异步执行任务转换为同步执行任务。

  • 当并行执行的处理更新数据时,会产生数据不一致的情况,有时候应用程序会因为内存错误导致异常结束的概率很高。虽然使用 Serial Dispatch Queue 和 dispatch_barrier_async 函数可以避免这类问题。

不考虑数据顺序,把所有数据插入数组中。

内存异常

通过Dispatch Semaphore进行线程安全控制

-(void)semaphore{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    NSMutableArray *array = [[NSMutableArray alloc]init];
    for (NSInteger i = 0; i < 100000; i++) {
        dispatch_async(queue, ^{
            /*一直等待,直到 Dispatch Semaphore 的计数值达到大于等于1*/
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            /*由于Dispatch Semaphore的计数值达到大于等于1,
             所以将Dispatch Semaphore的计数值减去1,
             dispatch_semaphore_wait 函数执行返回
             执行到此时的 Dispatch Semaphore 计数值恒为0
             
             由于可访问的 NSMutableArray 类对象的线程只有1个 因此可以进行安全的更新*/
            [array addObject:@(i)];
            /*排他控制结束,所以通过 dispatch_semaphore_signal 函数将 Dispatch Semaphore 的计数值加1
             如果有通过 dispatch_semaphore_wait 函数等待 Dispatch Semaphore 的计数值增加的线程,
             就由最先等待的线程执行*/
            dispatch_semaphore_signal(semaphore);
        });
    }
}
  • Dispatch Semaphore 线程安全和线程同步(为线程加锁)
    场景:总共有50张火车票,有两个售卖火车票的窗口,一个是北京火车票售卖窗口,另一个是上海火车票售卖窗口。两个窗口同时售卖火车票,卖完为止。
/**
 * 非线程安全:不使用 semaphore
 * 初始化火车票数量、卖票窗口(非线程安全)、并开始卖票
 */
- (void)initTicketStatusNotSave {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"semaphore---begin");
    
    self.ticketSurplusCount = 50;
    
    // queue1 代表北京火车票售卖窗口
    dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
    // queue2 代表上海火车票售卖窗口
    dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
    
    __weak typeof(self) weakSelf = self;
    dispatch_async(queue1, ^{
        [weakSelf saleTicketNotSafe:@"北京"];
    });
    
    dispatch_async(queue2, ^{
        [weakSelf saleTicketNotSafe:@"上海"];
    });
}

/**
 * 售卖火车票(非线程安全)
 */
- (void)saleTicketNotSafe:(NSString*)who{
    while (1) {
        if (self.ticketSurplusCount > 0) {  //如果还有票,继续售卖
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@====%@", (long)self.ticketSurplusCount,who, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        } else { //如果已卖完,关闭售票窗口
            NSLog(@"所有火车票均已售完");
            break;
        }
    }
}

在不考虑线程安全的前提下数据会产生错乱。

剩余票数:24 窗口:上海====<NSThread: 0x129de24f0>{number = 5, name = (null)}
剩余票数:22 窗口:北京====<NSThread: 0x129de1df0>{number = 4, name = (null)}
剩余票数:23 窗口:上海====<NSThread: 0x129de24f0>{number = 5, name = (null)}
剩余票数:21 窗口:上海====<NSThread: 0x129de24f0>{number = 5, name = (null)}

使用信号量进行优化

/**
 * 线程安全:使用 semaphore 加锁
 * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票
 */
- (void)initTicketStatusSave {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"semaphore---begin");
    
    semaphoreLock = dispatch_semaphore_create(1);//信号总量不为0,正常执行
    
    self.ticketSurplusCount = 50;
    
    // queue1 代表北京火车票售卖窗口
    dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_CONCURRENT);
    // queue2 代表上海火车票售卖窗口
    dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_CONCURRENT);
    
    __weak typeof(self) weakSelf = self;
    dispatch_async(queue1, ^{
        [weakSelf saleTicketSafe:@"北京"];
    });
    
    dispatch_async(queue2, ^{
        [weakSelf saleTicketSafe:@"上海"];
    });
}

/**
 * 售卖火车票(线程安全)
 */
- (void)saleTicketSafe:(NSString*)who {
    while (1) {
        dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);// 相当于加锁
        
        if (self.ticketSurplusCount > 0) {  //如果还有票,继续售卖
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@====%@", (long)self.ticketSurplusCount,who, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        } else { //如果已卖完,关闭售票窗口
            NSLog(@"所有火车票均已售完");
            dispatch_semaphore_signal(semaphoreLock);// 相当于解锁
            break;
        }
        dispatch_semaphore_signal(semaphoreLock);// 相当于解锁
    }
}

dispatch_once

void
dispatch_once(dispatch_once_t *predicate,
        DISPATCH_NOESCAPE dispatch_block_t block);

demo

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});

Dispatch I/O

在进行大为难读取时,将文件分割成合适大小并且使用 Global Dispatch Queue并列读取的话,会比一般读取速度更快。

/*将文件分割为一块一块急性读取处理,分割读取的数据通过使用 Dispatch Data 可更为简单的进行结合和分割*/
-(void)asyncIO{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{/*读取  0 ~ 8181 字节*/});
    dispatch_async(queue, ^{/*读取  8182 ~ 16383 字节*/});
    dispatch_async(queue, ^{/*读取  16384 ~ 46383 字节*/});
}
  • I/O create
//创建IO操作路径
dispatch_io_t
dispatch_io_create_with_path(dispatch_io_type_t type, // DISPATCH_IO_STREAM、DISPATCH_IO_RANDOM
const char *path,  //文件路径
int oflag, 
mode_t mode,
dispatch_queue_t queue,
void (^cleanup_handler)(int error));

dispatch_io_t
dispatch_io_create_with_io(dispatch_io_type_t type,
    dispatch_io_t io,
    dispatch_queue_t queue,
    void (^cleanup_handler)(int error));
  • I/O water 分割文件大小
// 最少读取量
void
dispatch_io_set_low_water(dispatch_io_t channel, size_t low_water);

// 最大读取量
void
dispatch_io_set_high_water(dispatch_io_t channel, size_t high_water);
  • I/O read 读取文件
void
dispatch_io_read(dispatch_io_t channel,
    off_t offset,
    size_t length,
    dispatch_queue_t queue,
    dispatch_io_handler_t io_handler);
  • I/O write 将文件存储到指定路径
void
dispatch_io_write(dispatch_io_t channel,
    off_t offset,
    dispatch_data_t data,
    dispatch_queue_t queue,
    dispatch_io_handler_t io_handler);
  • 异步串行读取文件
  • 异步并行读取文件

Dispatch Source

dispatch_source_t
dispatch_source_create(dispatch_source_type_t type,
    uintptr_t handle,
    unsigned long mask,
    dispatch_queue_t _Nullable queue);
  • 调用cancel 方法 响应方法:dispatch_source_set_cancel_handler
void
dispatch_source_cancel(dispatch_source_t source);

GCD Time

ull:unsigned long long,是C语言的数值字面量。

NSEC_PER_SEC : 和数值相乘得到单位为毫微秒数值。
NSEC_PER_MSEC:和数值相乘得到单位为毫秒
USEC_PER_SEC:每秒有多少毫秒。(注意是指在纳秒的基础上)
NSEC_PER_USEC:每毫秒有多少纳秒

other

DISPATCH_TIME_NOW
DISPATCH_TIME_FOREVER

//用于计算相对时间
dispatch_time_t
dispatch_time(dispatch_time_t when, //开始时间
int64_t delta //毫微秒单位的时间
);

//用于计算绝对时间:例如指定2019-11-11 11:11:00
dispatch_time_t
dispatch_walltime(const struct timespec *_Nullable when, int64_t delta);

通过NSDate 获取 dispatch_walltime 中的第一个参数。

dispatch_time_t getDispatchTimeBtDate(NSDate* date){
    NSTimeInterval interval;
    double second,subsecond;
    struct timespec time;
    dispatch_time_t milestone;
    
    interval = [date timeIntervalSince1970];
    subsecond = modf(interval, &second);
    time.tv_sec = second;
    time.tv_nsec = subsecond * NSEC_PER_SEC;
    milestone = dispatch_walltime(&time, 0);
    return milestone;
}
void
dispatch_source_set_event_handler(dispatch_source_t source,
    dispatch_block_t _Nullable handler);
void
dispatch_source_set_cancel_handler(dispatch_source_t source,
    dispatch_block_t _Nullable handler);
  • 延时操作
void
dispatch_after(dispatch_time_t when,
    dispatch_queue_t queue,//指定要追加处理的Dispatch Queue
    dispatch_block_t block);

demo 延时方法系统有指定的快捷键

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});

dispatch_after 函数并不是在指定时间后执行处理,而只是在指定时间追加到Dispatch Queue中,因为Main Dispatch Queue在主线程的RunLoop中执行,所以在比如每隔1/60秒执行的RunLoop中,Block最快在3秒后执行,最慢在3秒+1/60秒后执行,并且在 Main Dispatch Queue 有大量处理追加或者主线程的处理本身会有延迟时,这个时间会更长。

GCD中的一些概念

Critical Section 临界区:就是一段代码不能被并发执行,也就是,两个线程不能同时执行这段代码。这很常见,因为代码去操作一个共享资源,例如一个变量若能被并发进程访问,那么它很可能会变质(注:它的值不再可信)。
Deadlock 死锁:第一个线程不能完成是因为它在等待第二个线程的完成。但第二个线程也不能完成,因为它在等待第一个线程的完成。

Context Switch 上下文切换:在单个进程里切换执行不同的线程时存储与恢复执行状态的过程。这个过程在编写多任务应用时很普遍,但会带来一些额外的开销。

并发与并行:多核设备通过并行来同时执行多个线程;然而,为了使单核设备也能实现这一点,它们必须先运行一个线程,执行一个上下文切换,然后运行另一个线程或进程。这通常发生地足够快以致给我们并发执行地错觉。

基础创建锁方式

@synthesize
属性:atomic

多线程面临的挑战

资源共享

  • 资源可以是一个属性、一个对象,通用的内存、网络设备或者一个文件等等。

  • 示例:当第一个线程正在写入这个数据结构时,第二个线程却尝试读取这个数据结构,那么获取到的数据可能是新旧参半或者没有初始化。

  • 示例:比如仅仅用一个整型值来做计数器。在程序运行过程中,我们有两个并行线程 A 和 B,这两个线程都尝试着同时增加计数器的值。要想增加计数器的值,当前的必须被从内存中读出,然后增加计数器的值,最后还需要将这个增加后的值写回内存中。
    线程 A 和 B 都从内存中读取出了计数器的值,假设为 17 ,然后线程A将计数器的值加1,并将结果 18 写回到内存中。同时,线程B也将计数器的值加 1 ,并将结果 18 写回到内存中。实际上,此时计数器的值已经被破坏掉了,因为计数器的值 17 被加 1 了两次,而它的值却是 18。在多线程里面访问一个共享的资源,如果没有一种机制来确保在线程 A 结束访问一个共享资源之前,线程 B 就不会开始访问该共享资源的话,资源竞争的问题就总是会发生。

    资源共享

互斥锁

互斥访问:同一时刻,只允许一个线程访问某个特定资源。
在 Objective-C 中将属性以 atomic 的形式来声明,就能支持互斥锁了。

互斥访问

死锁

当多个线程在相互等待着对方的结束时,就会发生死锁,这时程序可能会被卡住。

资源饥饿(Starvation)

优先级反转

程序在运行时低优先级的任务阻塞了高优先级的任务,有效的反转了任务的优先级。

进程与线程

进程:正在运行的应用程序。进程之间的内存互相独立。
线程:线程是进程的基本执行单元。一个进程中的所有任务(代码)都在线程中执行,一个进程至少有1个线程。

线程安全:

线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。一般对访问的代码只进行读操作,默认是线程安全的,如果进行了写操作就要考虑线程安全了。
NSDictionary 。你可以在同一时间在多个线程中使用它而不会有问题。
NSMutableDictionary 就不是线程安全的,应该保证一次只能有一个线程访问它。

线程同步:

可理解为线程 A 和 线程 B 一块配合,A 执行到一定程度时要依靠线程 B 的某个结果,于是停下来,示意 B 运行;B 依言执行,再将结果给 A;A 再继续操作。

待续……

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容