iOS开发线程锁

前言

多线程是现在每个开发必定知道的。这哥们(多线程)是用来干啥的?举个例子拿单线程来说,单线程就是你写的代码一步一步执行,完全按照顺序执行,科技一步一步进步,现在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中都有哪些锁?

敲黑板,讲重点。
从大的方向讲有两种锁:

互斥锁

自旋锁。

这两种类型下分别有自己对应的锁:
NSLock.png

互斥锁和自旋锁的对比:

这两种锁的相同点不必多说,都可以避免多线程访问同一个值发生混乱,重点说一下两种的不同点:

互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁, 则等待资源的线程会被唤醒

自旋锁:如果共享数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁, 则等待资源的线程会立即执行

自旋锁的特点:

自旋锁的性能高于互斥锁,因为响应速度快
自旋锁虽然会一直自旋等待获取锁,但不会一直占用CPU,超过了操作系统分配的时间片会被强制挂起
自旋锁如果不能保证所有线程都是同一优先级,则可能造成死锁。

因为以上的特点,自旋锁和互斥锁也有不同的使用场景:

多核处理器情况下: 如果预计线程等待锁的时间比较短,短到比线程两次切换上下文的时间还要少的情况下,自旋锁是更好的选择。
如果时间比较长,则互斥锁是比较好的选择。 单核处理器情况下: 不建议使用自旋锁。

从详细来分锁:

  • @synchronized

  • NSLock 对象锁

  • NSRecursiveLock递归锁

  • NSConditionLock 条件锁

  • pthread_mutex 互斥锁(C语言)

  • dispatch_semaphore 信号量实现加锁(GCD)

  • OSSpinLock 自旋锁

具体使用请移步google。

你可能感兴趣的:(iOS开发线程锁)