最近看了一些关于ios下多线程的数据加锁保护代码。初略总结了几种常用的多线程下的加锁方法。
最常见的大概就是我们对于“加锁”最直观最了当的理解,NSLock。即用同一把锁对需要加锁保护的代码进行加解锁保护。
创建一个操作对象类 TestLockObj 。代码如下:
//
// TestLockObj.h
// Robin_Test_Learn
//
// Created by Jiabin He on 14-8-27.
// Copyright (c) 2014年 Robin. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface TestLockObj : NSObject
//多线程下操作的两个方法 method1 ,method2
- (void) method1;
- (void) method2;
@end
//
// TestLockObj.m
// Robin_Test_Learn
//
// Created by Jiabin He on 14-8-27.
// Copyright (c) 2014年 Robin. All rights reserved.
//
#import "TestLockObj.h"
@implementation TestLockObj
/**
* 跟踪查看这两个方法调用的前后顺序
* 打印出调用方法名称 NSStringFromSelector()
*/
-(void)method1{
NSLog(@"%@",NSStringFromSelector(_cmd));
}
-(void)method2{
NSLog(@"%@",NSStringFromSelector(_cmd));
}
@end
NSLock 的加解锁保护执行代码如下:
/**
* NSLock
*/
//创建一个锁对象,后面加解锁都必须用同一把锁进行操作
TestLockObj *testObj = [[TestLockObj alloc] init];
NSLock *lock = [[NSLock alloc] init];
//使用 dispatch_async() 函数开启一个线程1 传入dispatch_global_queue 全局队列,为并发队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
[testObj method1];
sleep(10);//为了达到后面的线程同步访问的效果
[lock unlock];
});
//使用 dispatch_async() 函数开启一个线程2 传入dispatch_global_queue 全局队列,为并发队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//这里休眠一秒,保证线程1首先被执行
[lock lock];
[testObj method2];
[lock unlock];
});
[lock release];
[testObj release];
执行结果为:
2014-10-09 15:40:40.283 Robin_Test_Learn[3311:144545] method1
2014-10-09 15:40:50.287 Robin_Test_Learn[3311:144546] method2
线程1首先被执行,执行后线程1先休眠10秒。线程2休眠1秒后也被执行,但是由于对象testObj被锁lock加锁保护,所以在线程1访问testObj期间,线程2只能阻塞,等待线程1执行访问完成后再继续执行。
介绍的第二种加锁方式就是使用 @synchronized()指令函数。
指令@synchronized()通过对一段代码的使用进行加锁,即这个指令可以将{ } 内的代码限制在一个线程执行。其他试图执行该段代码的线程都会被阻塞,直到加锁线程退出执行该段被保护的代码段。
指令@synchronized()需要一个参数。该参数可以使任何的Objective-C对象,包括self。这个对象就是互斥信号量。他能够让一个线程对一段代码进行保护,避免别的线程执行该段代码。
@synchronized()一般在公用变量的时候使用,如单例模式或者操作类的static变量中使用。
代码如下:
还是使用上面的操作对象类 TestLockObj
/**
* @synchronized
*/
TestLockObj *testObj = [[TestLockObj alloc] init];
//使用 dispatch_async() 函数开启一个线程1 传入dispatch_global_queue 全局队列,为并发队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(testObj){//testObj对象作为互斥信号量
[testObj method1];
sleep(10);
}
});
//使用 dispatch_async() 函数开启一个线程2 传入dispatch_global_queue 全局队列,为并发队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(testObj){//testObj对象作为同一个互斥信号量
[testObj method2];
}
});
需要注意的是上面两个线程对于@synchronized()指令的参数即互斥信号量必须是一致(testObj),才能达到线程间的互斥作用,达到加锁的功能。结果和上面的NSLock加锁效果一样。
介绍的第三种加锁方式就是使用 GCD中的Serial queues(即用户队列,串行队列)
代码如下:
还是使用上面的操作对象类 TestLockObj
/**
* serial_queues
*/
TestLockObj *testObj = [[TestLockObj alloc] init];
//创建serial_queues 用户队列,为串行队列
dispatch_queue_t myQueue = dispatch_queue_create("com.SerialQueue", Nil);
//使用 dispatch_async() 函数开启一个线程1 传入serial_queues 用户队列,为串行队列
dispatch_async(myQueue, ^{
[testObj method1];
sleep(10);
});
//使用 dispatch_async() 函数开启一个线程2 传入serial_queues 用户队列,为串行队列
dispatch_async(myQueue, ^{
sleep(1);
[testObj method2];
});
上面的代码中,首先创建一个serial_queues 用户队列(myQueue),该队列为串行队列。然后使用dispatch_async() 函数开启两个线程,将两个任务添加到同一个串行队列(myQueue)中。由于是串行队列,所以,添加到该队列中的任务都将按照添加的先后顺序依次执行,达到加锁的目的。
运行结果和上面两种加锁方法的一致。