前言
多线程是现在每个开发必定知道的。这哥们(多线程)是用来干啥的?举个例子拿单线程来说,单线程就是你写的代码一步一步执行,完全按照顺序执行,科技一步一步进步,现在CPU核心数越来越多,多线程也就成为了现在每个程序员必回的一个知识点。但是使用多线程优点就不多说了。要说都是优点没缺点,这纯瞎说,他的弊端就是资源抢占问题,开辟多条线程占用一定的资源(主线程一般1MB ,其他线程512kb,一般建议同时最多开三条线程比较合理)。关于资源抢占问题下面就举个例子:在使用多线程的时候多个线程可能会访问同一块资源,这样就很容易引发数据错乱和数据安全等问题。解决资源争用,最直接的想法是引入锁,对并发读写的数据进行保护,保证每次只有一个线程访问这一块资源。
锁是最常用的同步工具:一块公共资源在同一个时间只能允许被一个线程访问,比如一个线程A进入加锁资源之后,由于已经加锁,另一个线程B就无法访问,只有等待前一个线程A执行完后解锁,B线程才能访问加锁资源。
为什么需要锁?
以常见的火车站卖票为例,假设有20张票,有两个窗口同时售票:
- (void)ticketTest{
self.ticketsCount = 20;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 线程1
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self sellingTickets];//多线程售票
}
});
// 线程2
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self sellingTickets];//多线程售票
}
});
}
- (void)sellingTickets{
NSInteger oldMoney = self.ticketsCount;
sleep(0.2);
oldMoney -= 1;
self.ticketsCount = oldMoney;
NSLog(@"当前剩余票数-> %ld", oldMoney);
}
不加锁
2019-02-21 17:10:40.861358+0800 多线程测试[8167:20874670] 当前剩余票数-> 19
2019-02-21 17:10:40.861358+0800 多线程测试[8167:20874671] 当前剩余票数-> 19
2019-02-21 17:10:40.861723+0800 多线程测试[8167:20874671] 当前剩余票数-> 18
2019-02-21 17:10:40.861723+0800 多线程测试[8167:20874670] 当前剩余票数-> 18
2019-02-21 17:10:40.861851+0800 多线程测试[8167:20874670] 当前剩余票数-> 17
2019-02-21 17:10:40.861961+0800 多线程测试[8167:20874670] 当前剩余票数-> 16
2019-02-21 17:10:40.861989+0800 多线程测试[8167:20874671] 当前剩余票数-> 16
2019-02-21 17:10:40.862066+0800 多线程测试[8167:20874670] 当前剩余票数-> 15
2019-02-21 17:10:40.862222+0800 多线程测试[8167:20874670] 当前剩余票数-> 14
2019-02-21 17:10:40.863234+0800 多线程测试[8167:20874671] 当前剩余票数-> 13
2019-02-21 17:10:40.863958+0800 多线程测试[8167:20874670] 当前剩余票数-> 12
2019-02-21 17:10:40.864225+0800 多线程测试[8167:20874671] 当前剩余票数-> 11
2019-02-21 17:10:40.864529+0800 多线程测试[8167:20874670] 当前剩余票数-> 10
2019-02-21 17:10:40.865159+0800 多线程测试[8167:20874671] 当前剩余票数-> 9
2019-02-21 17:10:40.865498+0800 多线程测试[8167:20874670] 当前剩余票数-> 8
2019-02-21 17:10:40.865777+0800 多线程测试[8167:20874671] 当前剩余票数-> 7
2019-02-21 17:10:40.866747+0800 多线程测试[8167:20874670] 当前剩余票数-> 6
2019-02-21 17:10:40.866970+0800 多线程测试[8167:20874671] 当前剩余票数-> 5
2019-02-21 17:10:40.867402+0800 多线程测试[8167:20874671] 当前剩余票数-> 4
2019-02-21 17:10:40.867879+0800 多线程测试[8167:20874671] 当前剩余票数-> 3
不加锁时很明显数据发生了混乱。
iOS中都有哪些锁?
敲黑板,讲重点。
从大的方向讲有两种锁:
互斥锁
自旋锁。
这两种类型下分别有自己对应的锁:
互斥锁和自旋锁的对比:
这两种锁的相同点不必多说,都可以避免多线程访问同一个值发生混乱,重点说一下两种的不同点:
互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁, 则等待资源的线程会被唤醒
自旋锁:如果共享数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁, 则等待资源的线程会立即执行
自旋锁的特点:
自旋锁的性能高于互斥锁,因为响应速度快
自旋锁虽然会一直自旋等待获取锁,但不会一直占用CPU,超过了操作系统分配的时间片会被强制挂起
自旋锁如果不能保证所有线程都是同一优先级,则可能造成死锁。
因为以上的特点,自旋锁和互斥锁也有不同的使用场景:
多核处理器情况下: 如果预计线程等待锁的时间比较短,短到比线程两次切换上下文的时间还要少的情况下,自旋锁是更好的选择。
如果时间比较长,则互斥锁是比较好的选择。 单核处理器情况下: 不建议使用自旋锁。
从详细来分锁:
-
@synchronized
-
NSLock 对象锁
-
NSRecursiveLock递归锁
-
NSConditionLock 条件锁
-
pthread_mutex 互斥锁(C语言)
-
dispatch_semaphore 信号量实现加锁(GCD)
-
OSSpinLock 自旋锁
具体使用请移步google。
网友评论