NSInteger total = 0; - (void)threadNotSafe { for (NSInteger index = 0; index < 3; index++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ total += 1; NSLog(@"total: %ld", total); total -= 1; NSLog(@"total: %ld", total); }); } } //第一次输出: 2017-11-28 23:34:11.551570+0800 BasicDemo[75679:5312246] total: 1 2017-11-28 23:34:11.551619+0800 BasicDemo[75679:5312248] total: 3 2017-11-28 23:34:11.551618+0800 BasicDemo[75679:5312249] total: 2 2017-11-28 23:34:11.552120+0800 BasicDemo[75679:5312246] total: 2 2017-11-28 23:34:11.552143+0800 BasicDemo[75679:5312248] total: 1 2017-11-28 23:34:11.552171+0800 BasicDemo[75679:5312249] total: 0 //第二次输出 2017-11-28 23:34:55.738947+0800 BasicDemo[75683:5313401] total: 1 2017-11-28 23:34:55.738979+0800 BasicDemo[75683:5313403] total: 2 2017-11-28 23:34:55.738985+0800 BasicDemo[75683:5313402] total: 3 2017-11-28 23:34:55.739565+0800 BasicDemo[75683:5313401] total: 2 2017-11-28 23:34:55.739570+0800 BasicDemo[75683:5313402] total: 1 2017-11-28 23:34:55.739577+0800 BasicDemo[75683:5313403] total: 0 NSInteger total = 0; NSLock *lock = [NSLock new]; - (void)threadSafe { for (NSInteger index = 0; index < 3; index++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [lock lock]; total += 1; NSLog(@"total: %ld", total); total -= 1; NSLog(@"total: %ld", total); [lock unlock]; }); } } //第一次输出 2017-11-28 23:35:37.696614+0800 BasicDemo[75696:5314483] total: 1 2017-11-28 23:35:37.696928+0800 BasicDemo[75696:5314483] total: 0 2017-11-28 23:35:37.696971+0800 BasicDemo[75696:5314481] total: 1 2017-11-28 23:35:37.696995+0800 BasicDemo[75696:5314481] total: 0 2017-11-28 23:35:37.697026+0800 BasicDemo[75696:5314482] total: 1 2017-11-28 23:35:37.697050+0800 BasicDemo[75696:5314482] total: 0 //第二次输出 2017-11-28 23:36:01.790264+0800 BasicDemo[75700:5315159] total: 1 2017-11-28 23:36:01.790617+0800 BasicDemo[75700:5315159] total: 0 2017-11-28 23:36:01.790668+0800 BasicDemo[75700:5315161] total: 1 2017-11-28 23:36:01.790687+0800 BasicDemo[75700:5315161] total: 0 2017-11-28 23:36:01.790711+0800 BasicDemo[75700:5315160] total: 1 2017-11-28 23:36:01.790735+0800 BasicDemo[75700:5315160] total: 0
从上面能够看出,第一个函数第一次和第二次调用的结果不同,换句话说,不能肯定代码的运行顺序和结果,是线程不安全的;第二个函数第一次和第二次输出结果同样,能够肯定函数的执行结果,是线程安全的。html
线程不安全是因为多线程访问形成的,那么如何解决?算法
1.既然线程安全问题是由多线程引发的,那么,最极端的可使用单线程保证线程安全。api
2.线程安全是因为多线程访问和修改共享资源而引发不可预测的结果,所以,若是都是访问共享资源而不去修改共享资源也能够保证线程安全,好比:设置只读属性的全局变量。数组
3.使用锁。缓存
锁是最经常使用的同步工具。一段代码段在同一个时间只能容许被一个线程访问,好比一个线程A进入加锁代码以后因为已经加锁,另外一个线程B就没法访问,只有等待前一个线程A执行完加锁代码后解锁,B线程才能访问加锁代码。不要将过多的其余操做代码放到里面,不然一个线程执行的时候另外一个线程就一直在等待,就没法发挥多线程的做用了。安全
- (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; [lock lock]; if (imageNames.count>0) { imageName = [imageNames lastObject]; [imageNames removeObject:imageName]; } [lock unlock]; }
每一个iOS开发最先接触的线程锁就是@synchronized,代码简单。数据结构
- (void)getIamgeName:(int)index{ NSString *imageName; @synchronized(self) { if (imageNames.count>0) { imageName = [imageNames lastObject]; [imageNames removeObject:imageName]; } } }
dispatch_semaphore_t
GCD中信号量,也能够解决资源抢占问题,支持信号通知和信号等待。每当发送一个信号通知,则信号量+1;每当发送一个等待信号时信号量-1,;若是信号量为0则信号会处于等待状态,直到信号量大于0开始执行。多线程
#import "DispatchSemaphoreViewController.h" @interface DispatchSemaphoreViewController () { dispatch_semaphore_t semaphore; } @end @implementation DispatchSemaphoreViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. semaphore = dispatch_semaphore_create(1); /** * 建立一个信号量为1的信号 * */ } - (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; /** * semaphore:等待信号 DISPATCH_TIME_FOREVER:等待时间 wait以后信号量-1,为0 */ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); if (imageNames.count>0) { imageName = [imageNames lastObject]; [imageNames removeObject:imageName]; } /** * 发送一个信号通知,这时候信号量+1,为1 */ dispatch_semaphore_signal(semaphore); } @end
NSCondition一样实现了NSLocking协议,因此它和NSLock同样,也有NSLocking协议的lock和unlock方法,能够当作NSLock来使用解决线程同步问题,用法彻底同样。并发
- (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; [lock lock]; if (imageNames.count>0) { imageName = [imageNames lastObject]; [imageNames removeObject:imageName]; } [lock unlock]; }
- (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; [lock lock]; //加锁 static int m = 0; static int n = 0; static int p = 0; NSLog(@"removeObjectBegin count: %ld\n",imageNames.count); if (imageNames.count>0) { imageName = [imageNames lastObject]; [imageNames removeObjectAtIndex:0]; m++; NSLog(@"执行了%d次删除操做",m); } else { p++; NSLog(@"执行了%d次等待",p); [lock wait]; //等待 imageName = [imageNames lastObject]; [imageNames removeObjectAtIndex:0]; /** * 有时候点击取出图片会崩溃 */ n++; NSLog(@"执行了%d次继续操做",n); } NSLog(@"removeObject count: %ld\n",imageNames.count); [lock unlock]; //解锁 } - (void)createImageName:(NSMutableArray *)imageNames{ [lock lock]; static int m = 0; [imageNames addObject:@"0"]; m++; NSLog(@"添加了%d次",m); [lock signal]; //唤醒随机一个线程取消等待继续执行 // [lock broadcast]; //唤醒全部线程取消等待继续执行 NSLog(@"createImageName count: %ld\n",imageNames.count); [lock unlock]; } #pragma mark - 多线程取出图片后删除 - (void)getImageNameWithMultiThread{ [lock broadcast]; NSMutableArray *imageNames = [[NSMutableArray alloc]init]; dispatch_group_t dispatchGroup = dispatch_group_create(); __block double then, now; then = CFAbsoluteTimeGetCurrent(); for (int i=0; i<10; i++) { dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){ [self getIamgeName:imageNames]; }); dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){ [self createImageName:imageNames]; }); } dispatch_group_notify(dispatchGroup, self.synchronizationQueue, ^(){ now = CFAbsoluteTimeGetCurrent(); printf("thread_lock: %f sec\nimageNames count: %ld\n", now-then,imageNames.count); }); }
也有人说这是个互斥锁
NSConditionLock一样实现了NSLocking协议,试验过程当中发现性能很低。async
- (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; [lock lock]; if (imageNames.count>0) { imageName = [imageNames lastObject]; [imageNames removeObject:imageName]; } [lock unlock]; }
NSConditionLock也能够像NSCondition同样作多线程之间的任务等待调用,并且是线程安全的。
- (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; [lock lockWhenCondition:1]; //加锁 if (imageNames.count>0) { imageName = [imageNames lastObject]; [imageNames removeObjectAtIndex:0]; } [lock unlockWithCondition:0]; //解锁 } - (void)createImageName:(NSMutableArray *)imageNames{ [lock lockWhenCondition:0]; [imageNames addObject:@"0"]; [lock unlockWithCondition:1]; } #pragma mark - 多线程取出图片后删除 - (void)getImageNameWithMultiThread{ NSMutableArray *imageNames = [[NSMutableArray alloc]init]; dispatch_group_t dispatchGroup = dispatch_group_create(); __block double then, now; then = CFAbsoluteTimeGetCurrent(); for (int i=0; i<10000; i++) { dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){ [self getIamgeName:imageNames]; }); dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){ [self createImageName:imageNames]; }); } dispatch_group_notify(dispatchGroup, self.synchronizationQueue, ^(){ now = CFAbsoluteTimeGetCurrent(); printf("thread_lock: %f sec\nimageNames count: %ld\n", now-then,imageNames.count); }); }
有时候“加锁代码”中存在递归调用,递归开始前加锁,递归调用开始后会重复执行此方法以致于反复执行加锁代码最终形成死锁,这个时候可使用递归锁来解决。使用递归锁能够在一个线程中反复获取锁而不形成死锁,这个过程当中会记录获取锁和释放锁的次数,只有最后二者平衡锁才被最终释放。
- (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; [lock lock]; if (imageNames.count>0) { imageName = [imageNames firstObject]; [imageNames removeObjectAtIndex:0]; [self getIamgeName:imageNames]; } [lock unlock]; } - (void)getImageNameWithMultiThread{ NSMutableArray *imageNames = [NSMutableArray new]; int count = 1024*10; for (int i=0; i<count; i++) { [imageNames addObject:[NSString stringWithFormat:@"%d",i]]; } dispatch_group_t dispatchGroup = dispatch_group_create(); __block double then, now; then = CFAbsoluteTimeGetCurrent(); dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){ [self getIamgeName:imageNames]; }); dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){ now = CFAbsoluteTimeGetCurrent(); printf("thread_lock: %f sec\nimageNames count: %ld\n", now-then,imageNames.count); }); }
NSDistributedLock是MAC开发中的跨进程的分布式锁,底层是用文件系统实现的互斥锁。NSDistributedLock没有实现NSLocking协议,因此没有lock方法,取而代之的是非阻塞的tryLock方法。
NSDistributedLock *lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/lock.lock"]; while (![lock tryLock]) { sleep(1); } //do something [lock unlock];
#import <pthread.h>
声明并初始化一个pthread_mutex_t的结构。使用pthread_mutex_lock和pthread_mutex_unlock函数。调用pthread_mutex_destroy来释放该锁的数据结构。
#import <pthread.h> @interface MYPOSIXViewController () { pthread_mutex_t mutex; //声明pthread_mutex_t的结构 } @end @implementation MYPOSIXViewController - (void)dealloc{ pthread_mutex_destroy(&mutex); //释放该锁的数据结构 } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. pthread_mutex_init(&mutex, NULL); /** * 初始化 * */ } - (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; /** * 加锁 */ pthread_mutex_lock(&mutex); if (imageNames.count>0) { imageName = [imageNames firstObject]; [imageNames removeObjectAtIndex:0]; } /** * 解锁 */ pthread_mutex_unlock(&mutex); }
POSIX还能够建立条件锁,提供了和NSCondition同样的条件控制,初始化互斥锁同时使用pthread_cond_init来初始化条件数据结构,
// 初始化 int pthread_cond_init (pthread_cond_t *cond, pthread_condattr_t *attr); // 等待(会阻塞) int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mut); // 定时等待 int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mut, const struct timespec *abstime); // 唤醒 int pthread_cond_signal (pthread_cond_t *cond); // 广播唤醒 int pthread_cond_broadcast (pthread_cond_t *cond); // 销毁 int pthread_cond_destroy (pthread_cond_t *cond);
首先要提的是OSSpinLock已经出现了BUG,致使并不能彻底保证是线程安全的。
#import <libkern/OSAtomic.h>
#import <libkern/OSAtomic.h> @interface MYOSSpinLockViewController () { OSSpinLock spinlock; //声明pthread_mutex_t的结构 } @end @implementation MYOSSpinLockViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. spinlock = OS_SPINLOCK_INIT; /** * 初始化 * */ } - (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; /** * 加锁 */ OSSpinLockLock(&spinlock); if (imageNames.count>0) { imageName = [imageNames firstObject]; [imageNames removeObjectAtIndex:0]; } /** * 解锁 */ OSSpinLockUnlock(&spinlock); } @end
dispatch_barrier_async/dispatch_barrier_sync在必定的基础上也能够作线程同步,会在线程队列中打断其余线程执行当前任务,也就是说只有用在并发的线程队列中才会有效,由于串行队列原本就是一个一个的执行的,你打断执行一个和插入一个是同样的效果。两个的区别是是否等待任务执行完成。
注意:若是在当前线程调用dispatch_barrier_sync打断会发生死锁。
@interface MYdispatch_barrier_syncViewController () { __block double then, now; } @end @implementation MYdispatch_barrier_syncViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. } - (void)getIamgeName:(NSMutableArray *)imageNames{ NSString *imageName; if (imageNames.count>0) { imageName = [imageNames firstObject]; [imageNames removeObjectAtIndex:0]; }else{ now = CFAbsoluteTimeGetCurrent(); printf("thread_lock: %f sec\nimageNames count: %ld\n", now-then,imageNames.count); } } - (void)getImageNameWithMultiThread{ NSMutableArray *imageNames = [NSMutableArray new]; int count = 1024*11; for (int i=0; i<count; i++) { [imageNames addObject:[NSString stringWithFormat:@"%d",i]]; } then = CFAbsoluteTimeGetCurrent(); for (int i=0; i<count+1; i++) { //100来测试锁有没有正确的执行 dispatch_barrier_async(self.synchronizationQueue, ^{ [self getIamgeName:imageNames]; }); } }
@synchronized:适用线程很少,任务量不大的多线程加锁 NSLock:其实NSLock并无想象中的那么差,不知道你们为何不推荐使用 dispatch_semaphore_t:使用信号来作加锁,性能提高显著 NSCondition:使用其作多线程之间的通讯调用不是线程安全的 NSConditionLock:单纯加锁性能很是低,比NSLock低不少,可是能够用来作多线程处理不一样任务的通讯调用 NSRecursiveLock:递归锁的性能出奇的高,可是只能做为递归使用,因此限制了使用场景 NSDistributedLock:由于是MAC开发的,就不讨论了 POSIX(pthread_mutex):底层的api,复杂的多线程处理建议使用,而且能够封装本身的多线程 OSSpinLock:性能也很是高,惋惜出现了线程问题 dispatch_barrier_async/dispatch_barrier_sync:测试中发现dispatch_barrier_sync比dispatch_barrier_async性能要高,真是大出意外