iOS中的锁详细介绍

一.前言

在开发中我们经常会使用多线程,多线程为我们带来了很大便利,提高了程序的执行效率,但同时也带来了data race。
data race 的定义很简单:当至少有两个线程同时访问同一个变量,而且至少其中一个是写操作时,就发生了data race。通过XCode来检测项目中存在的data race。


Snip20200616_1.png

所以这就要利用一些同步机制来确保数据的准确性,锁就是同步机制中的一种。

二.互斥锁和自选锁的概念

1.临界区段(Critical section): 一个访问共享资源(例如:共享设备或是共享存储器)的程序片段,而这些共享资源有无法同时被多个线程访问的特性。

2.互斥锁(Mutex): 一种用于多线程编程中,防止两条线程同时对同一公共资源进行读写的机制,该目的通过将代码切成一个一个的临界区而达成。当获取失败时,线程会进入睡眠,等待锁释放时被唤醒。互斥锁又分为递归锁和非递归锁。

递归锁:可重入锁,同一个线程在锁释放前,可再次获取锁,即可以递归调用。
非递归锁:不可重入,必须等锁释放后才能再次获取锁。

3.自旋锁(Spinlocks):多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显示释放自旋锁。

4.互斥锁和自选锁的区别
互斥锁的起始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥的开销造成影响,不占用cpu资源,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长。

5.锁的应用
互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况可以考虑
5.1 临界区IO操作
5.2 临界区代码复杂或者循环量大
5.3 临界区竞争非常激烈
5.4 单核处理器
至于自旋锁就主要用在临界区持锁时间非常短且CPU不紧张的情况下,自旋锁一般用于多核的服务器。

三.互斥锁

os_unfair_lock

os_unfair_lock 用于取代不安全的 OSSpinLock,从 iOS10 开始才支持 从底层调用看,等待 os_unfair_lock 锁的线程会处于休眠状态,并非忙等 需要导入头文件 #import <os/lock.h>。

    __block os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        os_unfair_lock_lock(&lock);
        NSLog(@"第一个线程同步操作开始");
        sleep(8);
        NSLog(@"第一个线程同步操作结束");
        os_unfair_lock_unlock(&lock);
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        os_unfair_lock_lock(&lock);
        NSLog(@"第二个线程同步操作开始");
        sleep(1);
        NSLog(@"第二个线程同步操作结束");
        os_unfair_lock_unlock(&lock);
    });

pthread_mutex

pthread_mutex 是 C 语言下多线程加互斥锁的方式,那来段 C 风格的示例代码,需要 #import <pthread.h>

int pthread_mutex_init(pthread_mutex_t * __restrict, const pthread_mutexattr_t * __restrict);

首先是第一个方法,这是初始化一个锁,__restrict 为互斥锁的类型,传 NULL 为默认类型,一共有 4 类型。

PTHREAD_MUTEX_NORMAL 缺省类型,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后先进先出原则获得锁。

PTHREAD_MUTEX_ERRORCHECK 检错锁,如果同一个线程请求同一个锁,则返回 EDEADLK,否则与普通锁类型动作相同。这样就保证当不允许多次加锁时不会出现嵌套情况下的死锁。

PTHREAD_MUTEX_RECURSIVE 递归锁,允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。

PTHREAD_MUTEX_DEFAULT 适应锁,动作最简单的锁类型,仅等待解锁后重新竞争,没有等待队列。

代码示例如下:

    __block pthread_mutex_t theLock;
    pthread_mutex_init(&theLock, NULL);
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            pthread_mutex_lock(&theLock);
            NSLog(@"需要线程同步的操作1 开始");
            sleep(3);
            NSLog(@"需要线程同步的操作1 结束");
            pthread_mutex_unlock(&theLock);
        
    });
       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(1);
            pthread_mutex_lock(&theLock);
            NSLog(@"需要线程同步的操作2");
            pthread_mutex_unlock(&theLock);
        
    });

通过pthread_mutex方式起到递归锁的效果

     __block pthread_mutex_t theLock;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        

        static void (^RecursiveMethod)(int);

        RecursiveMethod = ^(int value) {
            
            pthread_mutex_lock(&theLock);
            if (value > 0) {
                
                NSLog(@"value = %d", value);
                sleep(1);
                RecursiveMethod(value - 1);
            }
            pthread_mutex_unlock(&theLock);
        };
        
        RecursiveMethod(5);
    });

注意:
pthread_mutex_trylock 和 tryLock 的区别在于,tryLock 返回的是 YES 和 NO,pthread_mutex_trylock 加锁成功返回的是 0,失败返回的是错误提示码。

NSLock

NSLock:是Foundation框架中以对象形式暴露给开发者的一种锁,(Foundation框架同时提供了NSConditionLock,NSRecursiveLock,NSCondition)NSLock定义如下:

@protocol NSLocking

- (void)lock;
- (void)unlock;

@end

@interface NSLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

@end

示例代码可参考如下:

//主线程中
    NSLock *lock = [[NSLock alloc] init];
      
      //线程1
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          [lock lock];
          NSLog(@"线程1");
          sleep(2);
          [lock unlock];
          NSLog(@"线程1解锁成功");
      });

      //线程2
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          sleep(1);//以保证让线程2的代码后执行
          [lock lock];
          NSLog(@"线程2");
          [lock unlock];
      });

tryLock 和 lock 方法都会请求加锁,唯一不同的是trylock在没有获得锁的时候可以继续做一些任务和处理。lockBeforeDate方法也比较简单,就是在limit时间点之前获得锁,没有拿到返回NO。

NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //[lock lock];
        [lock lockBeforeDate:[NSDate date]];
            NSLog(@"需要线程同步的操作1 开始");
            sleep(2);
            NSLog(@"需要线程同步的操作1 结束");
        [lock unlock];
        
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        if ([lock tryLock]) {//尝试获取锁,如果获取不到返回NO,不会阻塞该线程
            NSLog(@"锁可用的操作");
            [lock unlock];
        }else{
            NSLog(@"锁不可用的操作");
        }
        
        NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:3];
        if ([lock lockBeforeDate:date]) {//尝试在未来的3s内获取锁,并阻塞该线程,如果3s内获取不到恢复线程, 返回NO,不会阻塞该线程
            NSLog(@"没有超时,获得锁");
            [lock unlock];
        }else{
            NSLog(@"超时,没有获得锁");
        }
    });
NSCondition

NSCondition 是一个条件锁, 条件对象同时充当给定线程中的锁和检查点,锁在测试条件并执行条件触发的任务时保护代码。检查点行为要求在线程执行其任务之前条件为 true,当条件不为真时,线程阻塞,它保持阻塞状态,直到另一个线程发出条件对象的信号。
NSCondition的对象实际上作为一个锁和一个线程检查器,锁上之后其他线程也能上锁,而之后根据条件决定是否继续运行线程,即线程是否要进入waiting状态,经测试,NSCondition直接进入waiting状态,当其它线程中的该锁执行signal或者broadcast方法时,线程被唤醒,继续运行之后的方法。

定义如下:

@interface NSCondition : NSObject <NSLocking> {
@private
    void *_priv;
}

- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;

注意:
其中 signal 和 broadcast 方法的区别在于,signal 只是一个信号量,只能唤醒一个等待的线程,想唤醒多个就得多次调用,而 broadcast 可以唤醒所有在等待的线程。如果没有等待的线程,这两个方法都没有作用。

    NSCondition *lock = [[NSCondition alloc] init];
    NSMutableArray *array = [[NSMutableArray alloc] init];
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        while (!array.count) {
            [lock wait];
            NSLog(@"数组为空");
        }
        [array removeAllObjects];
        NSLog(@"array removeAllObjects");
        [lock unlock];
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);//以保证让线程2的代码后执行
        [lock lock];
        [array addObject:@1];
        NSLog(@"array addObject:@1");
        [lock signal];
        NSLog(@"发送signal");
        [lock unlock];
    });

NSConditionLock

NSConditionLock 是对 NSCondition 的进一步封装,可以设置具体的条件值对象。NSConditionLock 可以确保只有在满足特定条件时,线程才能获得锁。一旦获得了锁并执行了代码的关键部分,线程就可以放弃锁并将相关条件设置为新的内容。
定义:

@interface NSConditionLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;

@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;

NSConditionLock 可以称为条件锁,只有 condition 参数与初始化时候的 condition 相等,lock 才能正确进行加锁操作。而 unlockWithCondition: 并不是当 Condition 符合条件时才解锁,而是解锁之后,修改 Condition 的值,这个结论可以从下面的例子中得出。

        //主线程中
        NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0];
        
        //线程1
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [lock lockWhenCondition:1];
            NSLog(@"线程1");
            sleep(2);
            [lock unlock];
        });
        
        //线程2
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(1);//以保证让线程2的代码后执行
            if ([lock tryLockWhenCondition:0]) {
                NSLog(@"线程2");
                [lock unlockWithCondition:2];
                NSLog(@"线程2解锁成功");
            } else {
                NSLog(@"线程2尝试加锁失败");
            }
        });
        
        //线程3
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(2);//以保证让线程2的代码后执行
            if ([lock tryLockWhenCondition:2]) {
                NSLog(@"线程3");
                [lock unlock];  // [lock unlock]; 解锁成功且不改变 Condition 值。
                NSLog(@"线程3解锁成功");
            } else {
                NSLog(@"线程3尝试加锁失败");
            }
        });
        
        //线程4
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(3);//以保证让线程2的代码后执行
            if ([lock tryLockWhenCondition:2]) {
                NSLog(@"线程4");
                [lock unlockWithCondition:1];  // 解锁时将 Condition 改成 1
                NSLog(@"线程4解锁成功");
            } else {
                NSLog(@"线程4尝试加锁失败");
            }
        });

NSRecursiveLock

NSRecursiveLock是递归锁,它和NSLock的区别在于, NSRecursiveLock可以在一个线程中重复添加锁, NSRecursiveLock会记录上锁和解锁的次数,当二者平衡的时候,才会释放锁,其它线程才可以上锁成功。
定义如下:

@interface NSRecursiveLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

@end

NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        static void (^RecursiveMethod)(int);
        
        RecursiveMethod = ^(int value) {
            [recursiveLock lock];
            if (value > 0) {
                NSLog(@"value = %d", value);
                sleep(1);
                RecursiveMethod(value - 1);
            }
            [recursiveLock unlock];
        };
        
        RecursiveMethod(5);
    });
 // 在给定的时间之前去尝试请求一个锁
 - (BOOL)lockBeforeDate:(NSDate *)limit

 // 尝试去请求一个锁,并会立即返回一个布尔值,表示尝试是否成功
 - (BOOL)tryLock
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        static void (^RecursiveMethod)(int);

        RecursiveMethod = ^(int value) {

            [lock lock];
            if (value > 0) {

                NSLog(@"value = %d", value);
                sleep(2);
                RecursiveMethod(value - 1);
            }
            [lock unlock];
        };

        RecursiveMethod(5);
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        sleep(2);
        BOOL flag = [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
        if (flag) {
            NSLog(@"lock before date");

            [lock unlock];
        } else {
            NSLog(@"fail to lock before date");
        }
    });

@synchronized

实际项目中:AFNetworking中

@synchronized (self) {
        [_taskDict setObject:downloader forKey:urlString];
    }

四.自旋锁

atomic 属性修饰符

一般我们在开发中,大部分属性的声明都会加上 nonatomic, 以提高数据的读取效率(即不使用同步锁),那么为什么属性即使声明为 atomic 依然不能保证线程安全呢?我来到 atomic 的源码中就能发现其中奥秘。

void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
    reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    // ...省略其他修饰符对属性的操作代码

    if (!atomic) {
        // 不是 atomic 修饰
        oldValue = *slot;
        *slot = newValue;
    } else {
        // 如果是 atomic 修饰,加一把同步锁,保证 setter 的安全
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }
}

id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
    // ...省略无关紧要的代码
    
    // 非原子属性,直接返回值
    if (!atomic) return *slot;
    // 原子属性,加同步锁,保证 getter 的安全
    spinlock_t& slotlock = PropertyLocks[slot];
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();
}

通过上述源码可以得出:属性在调用 getter 和 setter 方法时,会加上同步锁, 即在属性在调用 getter 和 setter 方法时,保证同一时刻只能有一个线程调用属性的读/写方法。保证了读和写的过程是可靠的,但并不能保证数据一定是可靠的。

举例:定义属性 NSInteger i 是原子的,对 i 进行 i = i + 1; 操作就是不安全的。
该表达式需要三步操作:
1.读取 i 的值存入寄存器;
2.将 i 加 1;
3.修改 i 的值;
如果在第1步完成的时候,i 被其他线程修改了,那么表达式执行的结果就会与预期的不一样,也就是不安全的。
读写锁

读写锁实际是一种的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际CPU数。
读写锁具有以下特点:
同一时间,只能有一个线程进行写的操作。
同一时间,允许有多个线程进行读的操作。
同一时间,不允许既有写的操作,又有读的操作。

pthread_rwlock_t使用很简单,只需要在读之前使用pthread_rwlock_rdlock,读完解锁pthread_rwlock_unlock,写前pthread_rwlock_wrlock,写入完成之后pthread_rwlock_unlock,任务都执行完了可以选择销毁pthread_rwlock_destroy或者等待下次使用。

读写锁的 API 使用:

// 需要导入头文件
#include <pthread.h>

pthread_rwlock_t lock;
// 初始化锁
pthread_rwlock_init(&lock, NULL);
// 读-加锁
pthread_rwlock_rdlock(&lock);
// 读-尝试加锁
pthread_rwlock_tryrdlock(&lock);
// 写-加锁
pthread_rwlock_wrlock(&lock);
// 写-尝试加锁
pthread_rwlock_trywrlock(&lock);
// 解锁
pthread_rwlock_unlock(&lock);
// 销毁
pthread_rwlock_destroy(&lock);

- (void)initReadWriteLock {
    //初始化读写锁
    pthread_rwlock_init(&_rwlock, NULL);
        
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        for (NSInteger i = 0; i < 5; i ++) {
            dispatch_async(queue, ^{
                [[[NSThread alloc]initWithTarget:self selector:@selector(readPthreadRWLock) object:nil]start];
                [[[NSThread alloc]initWithTarget:self selector:@selector(writePthreadRWLock) object:nil]start];
            });
        }
}

- (void)readPthreadRWLock {
//    NSLog(@"readPthreadRWLock -->%@", [NSThread currentThread]);
    pthread_rwlock_rdlock(&_rwlock);
    NSLog(@"读文件");
    sleep(10);
    pthread_rwlock_unlock(&_rwlock);
}
- (void)writePthreadRWLock {
//    NSLog(@"writePthreadRWLock -->%@", [NSThread currentThread]);
    pthread_rwlock_wrlock(&_rwlock);
    NSLog(@" 写入文件");
    sleep(1);
    pthread_rwlock_unlock(&_rwlock);
}


- (void)dealloc {
    pthread_rwlock_destroy(&_rwlock);//销毁锁
}

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

推荐阅读更多精彩内容

  • 本文节选自成长手册 文章推荐和参考深入理解 iOS 开发中的锁pthread的各种同步机制 多线程编程被普遍认为复...
    百草纪阅读 2,793评论 1 9
  • 前言 在多线程开发中,常会遇到多个线程访问修改数据。为了防止数据不一致或数据污染,通常采用加锁机制来保证线程安全。...
    赵梦楠阅读 908评论 0 5
  • iOS中的锁 前言 写在前面: 临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法。 自旋锁:是用于...
    ROBIN2015阅读 890评论 0 7
  • 怎么检测项目中的Data race? 下面就进入正题简单聊一聊iOS中的锁,以及相关的内容(由于本人能力有限,文中...
    SuperMario_Nil阅读 33,828评论 7 177
  • 目录 1、为什么要线程安全 2、自旋锁和互斥锁 3、锁的类型1、OSSpinLock2、os_unfair_loc...
    SunshineBrother阅读 1,162评论 0 20