IOS 多线程编程_NSLock,NSCondition,synchronized和生产者消费者模型
来源:互联网 发布:淘宝代购衣服是正品吗 编辑:程序博客网 时间:2024/05/05 01:10
1.NSLock 线程锁,
任何两个线程访问同一共享资源(变量,数组)都需要加锁,保证同一时刻只能有一个线程访问共享资源
一个银行账户:有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 FKAccountNSLock* 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 FKAccountNSCondition* 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 FKViewControllerFKAccount* 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
执行的结果为
无论线程执行多少次存取,结果总是对的
0 0
- IOS 多线程编程_NSLock,NSCondition,synchronized和生产者消费者模型
- 多线程编程之生产者和消费者模型
- NSCondition 多线程解决生产者消费者问题
- 多线程模型:生产者和消费者
- Python--多线程编程--生产者消费者模型
- 多线程---生产者-消费者模型
- java多线程之生产者和消费者模型
- java多线程--生产者和消费者模型
- NSCondition实现生产者消费者模式
- 生产者和消费者模型
- 生产者和消费者模型
- 多线程生产者和消费者
- 多线程,生产者和消费者
- 多线程,生产者和消费者
- 多线程--生产者和消费者
- 多线程之生产者消费者模型
- java多线程生产者、消费者模型
- 多线程之生产者消费者模型
- Effective cpp 读书笔记4
- oracle sql 解析过程
- iOS开发之实现模糊效果 Core Image 、 vImage 、UIVisualEffectView、FXBlurView、GPUImage、UIImage+ImageEffects
- LeetCode 217. Contains Duplicate
- Google升级Drive SDK,支持Android/iOS应用集成
- IOS 多线程编程_NSLock,NSCondition,synchronized和生产者消费者模型
- Eclipse中产生作者名字的快捷键
- 12.5.3 UNIVERSAL:最终的祖先类:
- NodeJs的阻塞和非阻塞
- mongodb 数据库操作--备份 还原 导出 导入
- HDU 2149 Public Sale
- LeetCode 169. Majority Element
- mysql workbench 快捷键
- Notepad++使用教程