一:基本概念
dispatch_async和dispatch_sync用来控制是否要开启新的线程;
特殊如果是主队列就是主线程,不会开启子线程;
// 队列的类型,决定了任务的执行方式(并发和串行)
1.并发队列
2.串行队列
3.主队列
sync代表当前队列;都是串行执行;
并发队列 手动创建的串行队列 主队列
sync 没有开启新线程 没有开启新线程 没有开启新线程
串行执行任务 串行执行任务 串行执行任务
async 有开启新线程 有开启新线程 没有开启新线程
并发执行 串行执行任务 串行任务
总结:死锁 使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列,产生死锁;
实例1:
// 会不会发生死锁? 会
NSLog(@"执行任务1");
// dispatch_sync 马上要在当前线程执行任务,执行完毕才能继续往下执行;
// 队列的特点:排队、FIFO、first in First out ,先进先出;
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2");
});
//整个test函数为一个task,队列必须先执行完test再去执行别的任务,与dispatch_sync立马执行冲突了
NSLog(@"执行任务3");
实例2:
// 会不会发生死锁? 不会
NSLog(@"执行任务1");
// dispatch_async 异步执行,由于传入的是主对列,不会开启新的线程,不要求立马在当前线程同步执行任务;
// 队列的特点:排队、FIFO、first in First out ,先进先出;
dispatch_queue_t queue = dispatch_get_main_queue();
// 等待任务test1执行完成以后,再去执行async任务;
dispatch_async(queue, ^{
NSLog(@"执行任务2");
});
NSLog(@"执行任务3");
实例3:
// 会不会发生死锁? 会
NSLog(@"执行任务1");
// dispatch_async 异步执行,由于传入的是主对列,不会开启新的线程,不要求立马在当前线程同步执行任务;
// 队列的特点:排队、FIFO、first in First out ,先进先出;
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
// dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_SERIAL);
// 等待任务test2执行完成以后,再去执行async任务;
dispatch_async(queue, ^{//0
NSLog(@"执行任务2");
dispatch_sync(queue2, ^{//1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
实例4:
// 会不会发生死锁? 会
NSLog(@"执行任务1");
// dispatch_async 异步执行,由于传入的是主对列,不会开启新的线程,不要求立马在当前线程同步执行任务;
// 队列的特点:排队、FIFO、first in First out ,先进先出;
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 等待任务test2执行完成以后,再去执行async任务;
// 并发队列可以同时执行多个任务,不需要等待一个任务执行完成以后再去执行另一个任务;
dispatch_async(queue, ^{//0
NSLog(@"执行任务2");
dispatch_sync(queue, ^{//1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
实例6:
dispatch_queue_t queue1 = dispatch_get_global_queue(0, 0);
NSLog(@"执行任务1");
dispatch_async(queue1, ^{
NSLog(@"1");
// [self performSelector:@selector(test7) withObject:nil afterDelay:3];
[[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
// self performSelector: withObject: afterDelay:的本质是添加定时器到runloop里面去; 使用runloop的函数;底层使用了定时器;添加定时器到runloop里面去;有效的前提是必须有runloop的。子线程里面没有runloop所以在子线程里面是不可以使用的;
// objc_msgSend();
NSLog(@"3");
锁:
OSSpinLock //自旋锁 等待锁的线程会处于忙等状态,一直占用着CPU的资源;
os_unfair_lock
pthread_mutex
dispathch_queue(DISPATHCH_QUEUE_SERIAL)
NSlock
NSrecursiveLock
NSCondition
NSconditionLock
@synchronized
static 初始化是编译时候赋值的,不可以直接跟函数;
自旋锁:
OSSpinLockLock(&__lock);//如果别的线程进来以后,等待锁释放,再进去;
等待:忙等和休眠;忙等就是while(1);一直占用着CPU;休眠是不占用CPU资源的;在内核态
多线程:线程调度线程,如果时间段,时间片轮转调度算法;
缺点:
线程优先级翻转;
如果等待锁的线程优先级比较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁;
os_unfair_lock:
os_unfair_lock 用于取代不安全的OSSpinLock 从iOS10开始支持
从底层看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等状态互斥锁
需要导入头文件 #import "os/lock.h"
互斥锁:
pthread_mutexattr_settype(&arrt, PTHREAD_MUTEX_DEFAULT);
mutex 互斥锁,等待锁的线程会处于休眠状态;跨平台的锁;
需要导入头文件 #import "os/lock.h"
递归锁:
pthread_mutexattr_settype(&arrt, PTHREAD_MUTEX_RECURSIVE);
为递归锁:允许同一个线程对一把锁进行重复加锁;注意必须是同一个线程,如果是有其他的程序还是需要等待的;
stepi是一步一步执行汇编语言的
低级锁是休眠的;
条件锁:
pthread_cond_t
q pthread_cond_signal(<#pthread_cond_t * _Nonnull#>) 1.唤醒条件;
广播唤醒多个线程条件
pthread_cond_broadcast(<#pthread_cond_t * _Nonnull#>)
信号量 :dispatch_semaphore_t
// 最大并发执行线程的数量;
self.semapath = dispatch_semaphore_create(5);
self.testThread = [[NSThread alloc]initWithBlock:^{
// 会判断的信号量的值>0。就让信号量的值减1,然后继续往下执行代码;
// 如果信号量的值《=0,就会休眠等待;
dispatch_semaphore_wait(self.semapath, DISPATCH_TIME_FOREVER);
NSLog(@"test-%@",[NSThreadcurrentThread]);
// 让信号量的值+1;
dispatch_semaphore_signal(self.semapath);
}];
读写锁(用要用于io文件读写)
同一时间,只能有1个线程进行写的操作
同一时间,允许有多个线程进行读的操作
同一时间,不允许既有写的操作,又有读的操作
读写互斥,可以多读;
pthread_rwlock_t
栅栏函数;也可以实现读写锁的操作;
dispatch_barrier_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
这个函数传入的并发队列必须是自己通过dispatch_queue_create自定义创建,不可以使用全局队列,或者串行队列、主队列;否则跟dispatch_async效果一样了。