IOS 多线程编程_NSLock,NSCondition,synchronized和生产者消费者模型
2016-05-18 13:54
495 查看
1.NSLock 线程锁,
任何两个线程访问同一共享资源(变量,数组)都需要加锁,保证同一时刻只能有一个线程访问共享资源
一个银行账户:有1000块钱,有两个线程同时做一次取钱操作,取钱的金额为800,这是就需要加锁,英文访问的是同一个银行账号
如果不加锁
运行结果
这样的结果是不安全的
所以银行账户需要加锁
调用
运行结果:
2.同步锁synchronized
执行结果
以上使用的主函数调用都一样的,只是FKAccount的实现方式不一样
3.NSCondition
使用NSCondition,实现多线程的同步,即,可实现生产者消费者问题。
基本思路是,首先要创建公用的NSCondition实例。然后:
消费者取得锁,取产品,如果没有,则wait,这时会释放锁,直到有线程唤醒它去消费产品;
生产者制造产品,首先也是要取得锁,然后生产,再发signal,这样可唤醒wait的消费者。
以银行账号为例子
有存钱,有取钱操作,这个时候需要NSCondition
存取钱操作
执行的结果为
无论线程执行多少次存取,结果总是对的
任何两个线程访问同一共享资源(变量,数组)都需要加锁,保证同一时刻只能有一个线程访问共享资源
一个银行账户:有1000块钱,有两个线程同时做一次取钱操作,取钱的金额为800,这是就需要加锁,英文访问的是同一个银行账号
如果不加锁
@implementation FKAccount - (id)initWithAccountNo:(NSString*)aAccount balance:(CGFloat)aBalance { self = [super init]; if (self) { _accountNo = aAccount; _balance = aBalance; } return self; } // 提供一个draw方法来完成取钱操作 - (void) draw:(CGFloat)drawAmount { // 账户余额大于取钱数目 if (self.balance >= drawAmount) { // 吐出钞票 NSLog(@"%@取钱成功!吐出钞票:%g", [NSThread currentThread].name , drawAmount); // [NSThread sleepForTimeInterval:0.001]; // 修改余额 _balance = _balance - drawAmount; NSLog(@"\t余额为: %g" , self.balance); } else { NSLog(@"%@取钱失败!余额不足!", [NSThread currentThread].name); } } - (NSUInteger) hash { return [self.accountNo hash]; } - (BOOL)isEqual:(id)anObject { if(self == anObject) return YES; if (anObject != nil && [anObject class] == [FKAccount class]) { FKAccount* target = (FKAccount*)anObject; return [target.accountNo isEqualToString:self.accountNo]; } return NO; }
运行结果
这样的结果是不安全的
所以银行账户需要加锁
// 封装账户编号、账户余额两个属性 @implementation FKAccount NSLock* lock; - (id)init { self = [super init]; if (self) { lock = [[NSLock alloc] init]; } return self; } - (id)initWithAccountNo:(NSString*)aAccount balance:(CGFloat)aBalance { self = [super init]; if (self) { lock = [[NSLock alloc] init]; _accountNo = aAccount; _balance = aBalance; } return self; } // 提供一个线程安全的draw方法来完成取钱操作 - (void) draw:(CGFloat)drawAmount { // 显式锁定lock对象 [lock lock]; // 账户余额大于取钱数目 if (self.balance >= drawAmount) { // 吐出钞票 NSLog(@"%@取钱成功!吐出钞票:%g", [NSThread currentThread].name , drawAmount); [NSThread sleepForTimeInterval:0.001]; // 修改余额 _balance = _balance - drawAmount; NSLog(@"\t余额为: %g" , self.balance); } else { NSLog(@"%@取钱失败!余额不足!", [NSThread currentThread].name); } // 释放lock的锁定 [lock unlock]; } - (NSUInteger) hash { return [self.accountNo hash]; } - (BOOL)isEqual:(id)anObject { if(self == anObject) return YES; if (anObject != nil && [anObject class] == [FKAccount class]) { FKAccount* target = (FKAccount*)anObject; return [target.accountNo isEqualToString:self.accountNo]; } return NO; } @end
调用
FKAccount* account; - (void)viewDidLoad { [super viewDidLoad]; // 创建一个账号 account = [[FKAccount alloc] initWithAccountNo:@"321231" balance: 1000.0]; } - (IBAction)draw:(id)sender { // 创建第1个线程对象 NSThread* thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(drawMethod:) object:[NSNumber numberWithInt:800]]; // 创建第2个线程对象 NSThread* thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(drawMethod:) object:[NSNumber numberWithInt:800]]; // 启动2条线程 [thread1 start]; [thread2 start]; } - (void) drawMethod:(NSNumber*) drawAmount { // 直接调用account对象的draw方法来执行取钱 [account draw:drawAmount.doubleValue]; }
运行结果:
2.同步锁synchronized
@implementation FKAccount - (id)initWithAccountNo:(NSString*)aAccount balance:(CGFloat)aBalance { self = [super init]; if (self) { _accountNo = aAccount; _balance = aBalance; } return self; } // 提供一个线程安全的draw方法来完成取钱操作 - (void) draw:(CGFloat)drawAmount { // 使用self作为同步监视器,任何线程进入下面同步代码块之前, // 必须先获得对self账户的锁定——其他线程无法获得锁,也就无法修改它 // 这种做法符合:“加锁 → 修改 → 释放锁”的逻辑 @synchronized(self) { // 账户余额大于取钱数目 if (self.balance >= drawAmount) { // 吐出钞票 NSLog(@"%@取钱成功!吐出钞票:%g", [NSThread currentThread].name , drawAmount); [NSThread sleepForTimeInterval:0.001]; // 修改余额 _balance = _balance - drawAmount; NSLog(@"\t余额为: %g" , self.balance); } else { NSLog(@"%@取钱失败!余额不足!", [NSThread currentThread].name); } }//同步代码块结束,该线程释放同步锁 } - (NSUInteger) hash { return [self.accountNo hash]; } - (BOOL)isEqual:(id)anObject { if(self == anObject) return YES; if (anObject != nil && [anObject class] == [FKAccount class]) { FKAccount* target = (FKAccount*)anObject; return [target.accountNo isEqualToString:self.accountNo]; } return NO; }
执行结果
以上使用的主函数调用都一样的,只是FKAccount的实现方式不一样
3.NSCondition
使用NSCondition,实现多线程的同步,即,可实现生产者消费者问题。
基本思路是,首先要创建公用的NSCondition实例。然后:
消费者取得锁,取产品,如果没有,则wait,这时会释放锁,直到有线程唤醒它去消费产品;
生产者制造产品,首先也是要取得锁,然后生产,再发signal,这样可唤醒wait的消费者。
以银行账号为例子
有存钱,有取钱操作,这个时候需要NSCondition
@implementation FKAccount NSCondition* cond; BOOL flag; - (id)init { self = [super init]; if (self) { cond = [[NSCondition alloc] init]; } return self; } - (id)initWithAccountNo:(NSString*)aAccount balance:(CGFloat)aBalance { self = [super init]; if (self) { cond = [[NSCondition alloc] init]; _accountNo = aAccount; _balance = aBalance; } return self; } // 提供一个线程安全的draw方法来完成取钱操作 - (void) draw:(CGFloat)drawAmount { // 加锁 [cond lock]; // 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞 if (!flag) { [cond wait]; } else { // 执行取钱 NSLog(@"%@ 取钱:%g" , [NSThread currentThread].name ,drawAmount); _balance -= drawAmount; NSLog(@"账户余额为:%g" , self.balance); // 将标识账户是否已有存款的旗标设为NO。 flag = NO; // 唤醒其他线程 [cond broadcast]; } [cond unlock]; } - (void) deposit:(CGFloat) depositAmount { [cond lock]; // 如果flag为YES,表明账户中已有人存钱进去,则存钱方法阻塞 if(flag) // ① { [cond wait]; } else { // 执行存款 NSLog(@"%@ 存款:%g" , [NSThread currentThread].name , depositAmount); _balance += depositAmount; NSLog(@"账户余额为:%g" , self.balance); // 将表示账户是否已有存款的旗标设为YES flag = YES; // 唤醒其他线程 [cond broadcast]; } [cond unlock]; } - (NSUInteger) hash { return [self.accountNo hash]; } - (BOOL)isEqual:(id)anObject { if(self == anObject) return YES; if (anObject != nil && [anObject class] == [FKAccount class]) { FKAccount* target = (FKAccount*)anObject; return [target.accountNo isEqualToString:self.accountNo]; } return NO; } @end
存取钱操作
@implementation FKViewController FKAccount* account; - (void)viewDidLoad { [super viewDidLoad]; // 创建一个账号 account = [[FKAccount alloc] initWithAccountNo:@"321231" balance: 1000.0]; } - (IBAction)depositDraw:(id)sender { // 创建、启动3个存钱的线程 [NSThread detachNewThreadSelector:@selector(drawMethod:) toTarget:self withObject:[NSNumber numberWithDouble:800.0]]; [NSThread detachNewThreadSelector:@selector(drawMethod:) toTarget:self withObject:[NSNumber numberWithDouble:800.0]]; // 创建、启动存钱的线程 [NSThread detachNewThreadSelector:@selector(drawMethod:) toTarget:self withObject:[NSNumber numberWithDouble:800.0]]; // 创建、启动取钱的线程 [NSThread detachNewThreadSelector:@selector(depositMethod:) toTarget:self withObject:[NSNumber numberWithDouble:800.0]]; } - (void) drawMethod:(NSNumber*) drawAmount { [NSThread currentThread].name = @"甲"; // 重复100次执行取钱操作 for (int i = 0 ; i < 100 ; i++ ) { [account draw:drawAmount.doubleValue]; } } - (void) depositMethod:(NSNumber*) depositAmount { [NSThread currentThread].name = @"乙"; // 重复100次执行存款操作 for (int i = 0 ; i < 100 ; i++ ) { [account deposit:depositAmount.doubleValue]; } } @end
执行的结果为
无论线程执行多少次存取,结果总是对的
相关文章推荐
- 如何在多台机器上共享IOS证书
- IOS开发 CGAffineTransform相关函数
- iOS - dispatch group用法(dispatch_group_leave、dispatch_group_enter)
- iOS奔溃日志总结
- iOS热更新
- ios交互js
- iOS 与HTML5交互之捕捉HTML5按钮点击事件,获取webview上按钮的点击事件
- ios通过字符串获取类名
- GitHub 排名前 100 的安卓、iOS项目简介
- 对于iOS中 frame 和bounds的一点自己的认识
- iOS XMPP框架学习
- iOS普通字符串和UTF-8之间的相互转换
- iOS学习笔记之五--标题 frame属性和按钮属性
- iOS开发中variable is not assignable missing block的解决办法
- iOS开发往服务器Post一个json数据
- iOS开发之手势识别
- iOS开发之自定义表情键盘(组件封装与自动布局)
- iOS程序启动与运转
- iOS开发之画图板(贝塞尔曲线)
- IOS开发之自定义Button(集成三种回调模式)