前言

Data race(数据竞争)的发生条件:

两个或者更多线程在一个程序中,并发的访问同一数据
至少一个访问是写入操作
些线程都不使用任何互斥锁来控制这些访问
对数据的读取和修改产生了竞争,从而导致各种不可预计的问题。

简单例子

- (void)threadNotSafe {  
    __block NSInteger total = 0;  
    for (NSInteger index = 0; index < 3; index++) {  
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // 加锁  
            total += 1;  
            NSLog(@"index: %ld total: %ld", index, total);  
            total -= 1;  
            NSLog(@"index: %ld total: %ld", index, total);  
            // 解锁
        });  
    }  
}

若不加锁,total的值将出现错误

简单概念

1.临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法。
2.自旋锁:是用于多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁。 自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。
3.互斥锁(Mutex):是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区而达成。
4.读写锁:是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁) 用于解决多线程对公共资源读写问题。读操作可并发重入,写操作是互斥的。 读写锁通常用互斥锁、条件变量、信号量实现。
5.信号量(semaphore):是一种更高级的同步机制,互斥锁可以说是semaphore在仅取值0/1时的特例。信号量可以有更多的取值空间,用来实现更加复杂的同步,而不单单是线程间互斥。
6.条件锁:就是条件变量,当进程的某些资源要求不满足时就进入休眠,也就是锁住了。当资源被分配到了,条件锁打开,进程继续运行。

互斥锁

1).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

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

2).pthread_mutex:

导入头文件 < pthread.h >

pthread_mutex_t _lock;  
pthread_mutex_init(&_lock, NULL);  

pthread_mutex_lock(&_lock);  
pthread_mutex_unlock(&_lock);

3).@synchronized:

@synchronized(self) {  
    //数据操作  
}

自旋锁

1).OSSpinLock:

导入头文件 < libkern/OSSpinLockDeprecated.h >

OSSpinLock lock = OS_SPINLOCK_INIT;  

OSSpinLockLock(&lock);  
OSSpinLockUnlock(&lock);
已经废弃了,它在某一些场景下已经不安全了。

2).os_unfair_lock:
os_unfair_lock 是苹果官方推荐的替换OSSpinLock的方案,但是它在iOS10.0以上的系统才可以调用。

导入头文件 < os/lock.h >

os_unfair_lock_t unfairLock;  
unfairLock = &(OS_UNFAIR_LOCK_INIT);  

os_unfair_lock_lock(unfairLock);  
os_unfair_lock_unlock(unfairLock);

读写锁(共享-互斥锁)

pthread_rwlock:

导入头文件 < pthread.h >

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
//加读锁  
pthread_rwlock_rdlock(&rwlock);  
//加写锁  
pthread_rwlock_wrlock(&rwlock);  
//解锁  
pthread_rwlock_unlock(&rwlock);

递归锁

递归锁有一个特点,就是同一个线程可以加锁N次而不会引发死锁。

1).NSRecursiveLock:
与NSLock一样实现了NSLocking协议

2).pthread_mutex(recursive):

pthread_mutex_t lock;  
pthread_mutexattr_t attr;  
pthread_mutexattr_init(&attr);  
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);  

pthread_mutex_init(&lock, &attr);  
pthread_mutexattr_destroy(&attr);  
pthread_mutex_lock(&lock);  
pthread_mutex_unlock(&lock);

条件锁

1).NSCondition:
遵循NSLocking协议,使用的时候同样是lock,unlock加解锁,wait是傻等,waitUntilDate:方法是等一会,都会阻塞掉线程,signal是唤起一个在等待的线程,broadcast是广播全部唤起。

2).NSConditionLock:
基本同 NSCondition。

3).信号量
dispatch_semaphore:

dispatch_semaphore_t lock = dispatch_semaphore_create(1);  
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);  
dispatch_semaphore_signal(lock);