iOS-多线程(线程同步和线程安全)

线程同步和线程安全

线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作(更改变量),一般都需要考虑线程同步,否则的话就可能影响线程安全。
线程同步:可理解为线程 A 和 线程 B 一块配合,A 执行到一定程度时要依靠线程 B 的某个结果,于是停下来,示意 B 运行;B 依言执行,再将结果给 A;A 再继续操作。

非线程安全(例子)

总共有100张火车票,有两个售卖火车票的窗口,一个是北京火车票售卖窗口,另一个是上海火车票售卖窗口。两个窗口同时售卖火车票,卖完为止。

NSThread
/**
 * 初始化火车票数量、卖票窗口(非线程安全)、并开始卖票
 */
- (void)initTicketSaling{
    self.ticketSurplusCount = 100;
    self.ticketSaleWindowBJ = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.ticketSaleWindowBJ.name = @"北京火车票售票窗口";
    
    self.ticketSaleWindowSH = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.ticketSaleWindowSH.name = @"上海火车票售票窗口";

    [self.ticketSaleWindowBJ start];
    [self.ticketSaleWindowSH start];
}
/**
 * 售卖火车票(非线程安全)
 */
- (void)saleTicket{
    while (1) {
        if (self.ticketSurplusCount > 0){
            self.ticketSurplusCount --;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
            [NSThread sleepForTimeInterval:0.2];
        }else{
            NSLog(@"所有火车票均已售完");
            break;
        }
    }
}

通过观察打印结果,我们发现在不考虑线程安全的情况下,得到票数是错乱的,并且部分票出现了重复。

GCD
/**
 * 初始化火车票数量、卖票窗口(非线程安全)、并开始卖票
 */
- (void)initTicketSaling{
    self.ticketSurplusCount = 100;
    //北京火车票售卖窗口
    dispatch_queue_t queueBJ = dispatch_queue_create("com.jun.bj", DISPATCH_QUEUE_SERIAL);
    //上海火车票售卖窗口
    dispatch_queue_t queueSH = dispatch_queue_create("com.jun.sh", DISPATCH_QUEUE_SERIAL);
    __weak typeof(self) weakSelf = self;
    dispatch_async(queueBJ, ^{
        [weakSelf saleTicket];
    });
    dispatch_async(queueSH, ^{
        [weakSelf saleTicket];
    });
}
/**
 * 售卖火车票(非线程安全)
 */
- (void)saleTicket{
    while(1){
        if(self.ticketSurplusCount > 0){
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        }else{
            NSLog(@"所有火车票均已售完");
            break;
        }
    }
}

通过观察打印结果,我们发现在不考虑线程安全的情况下,得到票数是错乱的,并且部分票出现了重复。

线程安全

线程安全解决方案:可以给线程加锁,在一个线程执行该操作的时候,不允许其他线程进行操作。iOS 实现线程加锁有很多种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/get等等各种方式。
以下代码采用@synchronized方式。

NSThread
/**
 * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票
 */
- (void)initTicketSaling{
    self.ticketSurplusCount = 100;
    self.ticketSaleWindowBJ = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.ticketSaleWindowBJ.name = @"北京火车票售票窗口";
    self.ticketSaleWindowSH = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.ticketSaleWindowSH.name = @"上海火车票售票窗口";
    [self.ticketSaleWindowBJ start];
    [self.ticketSaleWindowSH start];
}
/**
 * 售卖火车票(线程安全)
 */
- (void)saleTicket{
    while (1) {
        // 互斥锁
        @synchronized (self) {
            if(self.ticketSurplusCount > 0){
                self.ticketSurplusCount --;
                NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
                [NSThread sleepForTimeInterval:0.2];
            }else{
                NSLog(@"所有火车票均已售完");
                break;
            }
        }
    }
}
GCD

以下代码采用dispatch_semaphore方式。

/**
 * 线程安全:使用 semaphore 加锁
 * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票
 */
- (void)initTicketSaling{
    self.semaphoreLock = dispatch_semaphore_create(1);
    self.ticketSurplusCount = 100;
    //北京火车票售卖窗口
    dispatch_queue_t queueBJ = dispatch_queue_create("com.jun.bj", DISPATCH_QUEUE_SERIAL);
    //上海火车票售卖窗口
    dispatch_queue_t queueSH = dispatch_queue_create("com.jun.sh", DISPATCH_QUEUE_SERIAL);
    __weak typeof(self) weakSelf = self;
    dispatch_async(queueBJ, ^{
        [weakSelf saleTicket];
    });
    dispatch_async(queueSH, ^{
        [weakSelf saleTicket];
    });
}
/**
 * 售卖火车票(线程安全)
 */
- (void)saleTicket{
    while (1) {
        dispatch_semaphore_wait(self.semaphoreLock, DISPATCH_TIME_FOREVER);
        if(self.ticketSurplusCount > 0){
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        }else{
            NSLog(@"所有火车票均已售完");
            dispatch_semaphore_signal(self.semaphoreLock);
            break;
        }
        dispatch_semaphore_signal(self.semaphoreLock);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值