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
原创粉丝点击