前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Objective 锁

Objective 锁

作者头像
老沙
发布2019-09-28 13:19:31
6490
发布2019-09-28 13:19:31
举报
文章被收录于专栏:老沙课堂

加锁

线程安全隐患解决方案

解决方案 使用线程同步技术(按照预定的先后次序进行)

1、OSSpinLock 自旋锁 ( heigh-leve Lock、自旋锁)

#import "<libkern/OSAtomic.h>"


代码语言:javascript
复制
 OSSpinLock _lock = OS_SPINLOCK_INIT;
 OSSpinLockLock(&_lock);
 OSSpinLockUnlock(&_lock);
 OSSpinLockTry(&_lock); //尝试加锁 如果不能加锁 返回No
复制代码
  • 因为是自旋锁( busy-wait) 所以一直cpu占用资源,线程不会休眠,一直等待,所以效率最高。
存在的问题
  • ios10之后过期
  • 优先级反转

由于锁是自旋锁,线程不会休眠,所以当低优先级线程先对操作进行Lock造作后,CPU调度高优先级线程造作,由于低优先级别UnLock就调用高优先级线程。高优先级无法处理该操作,而高优先级线程一直调用CPU资源, 系统等待高优先级线程执行完毕后才给低优先级线程资源。

高优先级线程等待低优先级线程Unlock。低优先级线程等待CPU资源调度。造成类似死锁 导致锁无法释放。相互等待

2、os_unfair_lock (low-level Lock、互斥锁 )

import <os.lock.h>

代码语言:javascript
复制
os_unfair_lock _lock = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&_lock);
os_unfair_lock_unlock(&_lock);
os_unfair_lock_trylock(&_lock);//尝试加锁 如果不能加锁 返回No
复制代码
  • ios10之后支持
3、ptheard_mutex (low-level Lock、互斥锁)

#import"pthread.h"

代码语言:javascript
复制
    pthread_mutexattr_t attr = {0};
    pthread_mutexattr_init(&attr);
		//设置锁的描述  PTHREAD_MUTEX_DEFAULT为一般互斥锁  PTHREAD_MUTEX_RECURSIVE为递归锁
		//pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE为递归锁); //递归锁
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    pthread_mutex_init(mutex, &attr);
		//释放  防止内存泄漏
    pthread_mutexattr_destroy(&attr);
复制代码
1、递归锁 PTHREAD_MUTEX_RECURSIVE

根据参数 attr PTHREAD_MUTEX_RECURSIVE 递归锁

PTHREAD_MUTEX_RECURSIVE :允许同一个线程 重复加锁。

2、条件 condition

线程激活信号 不一定会马上执行 先等上一次操作unlock

代码语言:javascript
复制
// 创建锁
- (void) __initLock:(pthread_mutex_t *) mutex {
    pthread_mutexattr_t attr = {0};
    pthread_mutexattr_init(&attr);
	  //互斥锁
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    pthread_mutex_init(mutex, &attr);
    pthread_mutexattr_destroy(&attr);
    
    pthread_condattr_t condAtt={0};
	  // 创建条件
    pthread_cond_init(&_condition, &condAtt);
    pthread_condattr_destroy(&condAtt);
}
- (instancetype)init
{
    self = [super init];
    if (self) {
        [self __initLock:&_lock];
        
        
       dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            [self test2];
        });
        dispatch_async(queue, ^{
            sleep(2);
            [self test1];
            
        });
    }
    return self;
}
- (void)test1 {
    NSLog(@"%@",[NSThread currentThread]);
  	// lock
    pthread_mutex_lock(&_lock);
    NSLog(@"1");
	  //对condition发送信号
    pthread_cond_signal( &_condition);
	  NSLog(@"3");
	  //unlock
    pthread_mutex_unlock(&_lock);
	  NSLog(@"4");
}

- (void)test2 {
    NSLog(@"%@",[NSThread currentThread]);
  	// lock
    pthread_mutex_lock(&_lock);
  	// 等待condition发送信号
    pthread_cond_wait(&_condition, &_lock);
    NSLog(@"2");
  	//unlock
    pthread_mutex_unlock(&_lock);
}

复制代码

输出结果为

代码语言:javascript
复制
2019-09-03 14:55:49.650729+0800 GCD[1918:36391522] 1
2019-09-03 14:55:49.650896+0800 GCD[1918:36391522] 3
2019-09-03 14:55:49.651025+0800 GCD[1918:36391522] 4
2019-09-03 14:55:49.651031+0800 GCD[1918:36391523] 2
复制代码

上述代码 无论test1 和test2谁先调用 test2 依赖于test1的调用 只有当test1中执行 pthread_cond_signal( &_condition); test2中才会继续执行, 执行步骤为:

  1. Test2
  2. Test2 中lock pthread_mutex_lock(&_lock);
  3. 等待condition发送信号pthread_cond_wait(&_condition, &_lock); 此时unlock锁 并且线程休眠
  4. 2s后执行Test1
  5. Test1 中lock pthread_mutex_lock(&_lock);
  6. 输出1
  7. //对condition发送信号 pthread_cond_signal( &_condition); test2线程被激活
  8. 输出3
  9. Test1中解锁unlock
  10. Test2 执行lock操作中继续执行 输出2
  11. test2中解锁
  • 8-9 和10-11是同时进行
  • condition 不仅可以发送信号signal 还可以发送广播 pthread_cond_boradcast 激活所有pthread_cond_wait
注意点
  1. 使用condition的时候 不要使用同一个线程调用wait和signal 因为线程调度万wait的时候为休眠状态,无法执行signal 可以试试上面的代码 把并发队列换成串行队列试一下。
  2. 结构体初始化只能直接初始化 如下图所示
4、NSLock

NSLock是对mutex PTHREAD_MUTEX_DEFAULT 普通锁的封装

代码语言:javascript
复制
- (void)lock;// 加锁
- (void)unlock;//解锁
- (BOOL)tryLock;// 尝试是否可以加锁 如果可以 直接加锁返回YES
- (BOOL)lockBeforeDate:(NSDate *)limit; //在一定时间内是否可以加锁 如果可以 直接加锁返回YES
复制代码
5、NSRecursiveLock

NSLock是对mutex PTHREAD_MUTEX_RECURSIVE递归锁的封装

API和NSLock一样

代码语言:javascript
复制
- (void)lock;// 加锁
- (void)unlock;//解锁
- (BOOL)tryLock;// 尝试是否可以加锁 如果可以 直接加锁返回YES
- (BOOL)lockBeforeDate:(NSDate *)limit; //在一定时间内是否可以加锁 如果可以 直接加锁返回YES
复制代码
6、NSCondition

NSCondition是对mutex和cond的封装

代码语言:javascript
复制
//用法和mutex condtion 一样  可以参考上面代码
- (void)lock;// 加锁
- (void)unlock;//解锁
- (void)wait;//等待
- (BOOL)waitUntilDate:(NSDate *)limit; //等待多久  如果超时就不等了 就直接执行
- (void)signal; // 信号量
- (void)broadcast; //广播
复制代码
7、NSConditionLock

NSConditionLock是对NSCondition的封装

代码语言:javascript
复制
- (void)lock;// 加锁
- (void)unlock;//解锁
- (void)lockWhenCondition:(NSInteger)condition; //休眠 直到符合conditon后  激活并lock
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition; // 解锁 修改condtion的值
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
复制代码
8、Dispatch_semaphore

Dispatch_semaphore 可以控制线程的个数 当控制线程个数为1的时候 能确保同时只有1条线程去访问,已达到确保线程安全的目的

代码语言:javascript
复制
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

// 如果信号量<=0 当前线程就会键入休眠状态 知道信号量的值>0
// 如果信号值>0 就-1 然后执行下面代码
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
[super __saleTicket];
// 信号量+1
dispatch_semaphore_signal(_semaphore);
复制代码
9、dispatch_quueue(DISPATCH_QUQUQ_SERIAL)

串行队列,当在一个串行队列执行的时候,只有一个线程,能确保线程安全

10、@synchronised

以前:对mutex递归锁的封装

现在:os_unfair_recursive_lock的封装

首先打开断点汇编模式

obj4源码中objc_sync.mm

底层存储 hash表 key :传进去的对象

代码语言:javascript
复制
class recursive_mutex_tt : nocopy_t {
    os_unfair_recursive_lock mLock;
	  ...
}
using recursive_mutex_t = recursive_mutex_tt<LOCKDEBUG>;

typedef struct alignas(CacheLineSize) SyncData {
    struct SyncData* nextData;
    DisguisedPtr<objc_object> object;
    int32_t threadCount;  // number of THREADS using this block
    recursive_mutex_t mutex;
} SyncData;

int objc_sync_enter(id obj)
{
    int result = OBJC_SYNC_SUCCESS;

    if (obj) {
        SyncData* data = id2data(obj, ACQUIRE);
        assert(data);
        data->mutex.lock();
    } else {
        // @synchronized(nil) does nothing
        if (DebugNilSync) {
            _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
        }
        objc_sync_nil();
    }

    return result;
}

复制代码
代码语言:javascript
复制
int objc_sync_exit(id obj)
{
    int result = OBJC_SYNC_SUCCESS;
    
    if (obj) {
        SyncData* data = id2data(obj, RELEASE);
        if (!data) {
            result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
        } else {
            bool okay = data->mutex.tryUnlock();
            if (!okay) {
                result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
            }
        }
    } else {
        // @synchronized(nil) does nothing
    }
	

    return result;
}
复制代码

至于耗时,测的时候每次都不太一样,大家自己测一下吧~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老沙说点事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 加锁
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档