您的位置:首页 > 移动开发 > IOS开发

iOS 多线程 各种锁

2016-03-17 21:23 330 查看
在iOS中有几种方法来解决多线程访问同一个内存地址的互斥同步问题:方法一,@synchronized(id anObject),(最简单的方法)会自动对参数对象加锁,保证临界区内的代码线程安全[cpp] view
plain copyprint?@synchronized(self){// 这段代码对其他 @synchronized(self) 都是互斥的// self 指向同一个对象}方法二,NSLockNSLock对象实现了NSLocking protocol,包含几个方法:lock,加锁unlock,解锁tryLock,尝试加锁,如果失败了,并不会阻塞线程,只是立即返回NOlockBeforeDate:,在指定的date之前暂时阻塞线程(如果没有获取锁的话),如果到期还没有获取锁,则线程被唤醒,函数立即返回NO比如:[cpp] view
plain copyprint?NSLock *theLock = [[NSLock alloc] init];if ([theLock lock]){//do something here[theLock unlock];}方法三,NSRecursiveLock,递归锁NSRecursiveLock,多次调用不会阻塞已获取该锁的线程。[cpp] view
plain copyprint?NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];void MyRecursiveFunction(int value){[theLock lock];if (value != 0)<span style="font-size:14px;"> </span>{–value;MyRecursiveFunction(value);}[theLock unlock];}MyRecursiveFunction(5);方法四,NSConditionLock,条件锁NSConditionLock,条件锁,可以设置条件[cpp] view
plain copyprint?//公共部分id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA];//线程一,生产者while(true) {[condLock lockWhenCondition:NO_DATA];//生产数据[condLock unlockWithCondition:HAS_DATA];}//线程二,消费者while (true) {[condLock lockWhenCondition:HAS_DATA];//消费[condLock unlockWithCondition:NO_DATA];}方法五,NSDistributedLock,分布锁NSDistributedLock,分布锁,文件方式实现,可以跨进程用tryLock方法获取锁。用unlock方法释放锁。如果一个获取锁的进程在释放锁之前挂了,那么锁就一直得不到释放了,此时可以通过breakLock强行获取锁iOS开发中不可避免的会遇到加锁的情况,今天就来比较一下各种加锁方式。目前我所知道的有如下几种加锁方式:1. @synchronized 关键字加锁2. NSLock 对象锁3. NSCondition4. NSConditionLock 条件锁5. NSRecursiveLock 递归锁6. pthread_mutex 互斥锁(C语言)7. dispatch_semaphore 信号量实现加锁(GCD)8. OSSpinLock这里分别使用8种方式加锁解锁1千万次,执行方法如下:?
执行结果:

由图可以发现:

OSSpinLock的性能最好,GCD的dispatch_semaphore紧随其后;

NSConditionLock和@synchronized性能较差;
PS:

1. 需要注意的是这里仅仅是对各种锁直接Lock和Unlock的性能测试,其中部分锁的使用条件上还是有细微的差异的,比如NSLock之类的还有tryLock等方法用于加锁,不同对象锁的功能偏向不一样等等,有兴趣的可以逐个搜索再更深入的研究不同锁之间的区别。

2. 另外,一般来说客户端很少会有这么大量的加锁解锁操作,所以日常来说这些锁的性能都是可以满足使用需求的。

1.单线程:禁用所有的mutex锁,并发使用时会出错。当SQLite编译时加了SQLITE_THREADSAFE=0参数,或者在初始化SQLite前调用sqlite3_config(SQLITE_CONFIG_SINGLETHREAD)时启用。2.多线程:只要一个数据库连接不被多个线程同时使用就是安全的。源码中是启用bCoreMutex,禁用bFullMutex。实际上就是禁用数据库连接和prepared statement(准备好的语句)上的锁,因此不能在多个线程中并发使用同一个数据库连接或prepared statement。当SQLite编译时加了SQLITE_THREADSAFE=2参数时默认启用。若SQLITE_THREADSAFE不为0,可以在初始化SQLite前,调用sqlite3_config(SQLITE_CONFIG_MULTITHREAD)启用;或者在创建数据库连接时,设置SQLITE_OPEN_NOMUTEX flag。3.串行:启用所有的锁,包括bCoreMutex和bFullMutex。因为数据库连接和prepared statement都已加锁,所以多线程使用这些对象时没法并发,也就变成串行了。当SQLite编译时加了SQLITE_THREADSAFE=1参数时默认启用。若SQLITE_THREADSAFE不为0,可以在初始化SQLite前,调用sqlite3_config(SQLITE_CONFIG_SERIALIZED)启用;或者在创建数据库连接时,设置SQLITE_OPEN_FULLMUTEX flag。
  在iOS上,默认使用的是第2种方式编译的,也就是只有一个线程能够打开数据库操作,其他线程要操作数据库必须等数据库关闭后才能打开操作。多线程时:每个线程独立打开数据库,操作数据库,操作完后关闭数据库。打开和关闭都比较费时间,而且要手动控制打开关闭锁,在每个线程操作不频率时可用该方法。  如果多个线程频繁操作数据库,使用以上方法很容易造成系统崩溃,解决方案:开启第3种串行模式,使用一个类(单例方式)操作数据库。
-(BOOL)open
{
sqlite3_shutdown();
NSLog(@"sqlite3 lib version: %s", sqlite3_libversion());
int err=sqlite3_config(SQLITE_CONFIG_SERIALIZED);
if (err == SQLITE_OK) {
NSLog(@"Can now use sqlite on multiple threads, using the same connection");
} else {
NSLog(@"setting sqlite thread safe mode to serialized failed!!! return code: %d", err);
}
err = sqlite3_open([VersionDBPath UTF8String], &hSqlite3DB);
if(err != SQLITE_OK) {
NSLog(@"datebase open error: %d", err);
return NO;
}
return YES;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: