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);
    }
}
发布了38 篇原创文章 · 获赞 5 · 访问量 9070

猜你喜欢

转载自blog.csdn.net/zj382561388/article/details/103133877