多线程线程资源共享问题

来源:互联网 发布:淘宝上的面膜是正品吗 编辑:程序博客网 时间:2024/06/11 19:12

简介

在多线程的环境下, 由于公共资源可能会被多个线程共享, 也就是多个线程可能会操作( 增、删、改等 )同一资源.
当多个线程操作同一块资源时, 很容易导致数据错乱或发生数据安全问题, 即:
数据有可能丢失, 有可能增加, 有可能错乱.

资源共享经典问题–>卖票

逻辑伪代码:
if( 余票 > 0 )
……余票-1
else
……提示无票

代码

Swift

var votes = 10override func viewDidLoad() {    super.viewDidLoad()    // 售票    self.saleTickets()}@objc func saleTickets() {    while true {        // 模拟耗时操作        Thread.sleep(forTimeInterval: 1.0)        if(self.votes > 0) {            self.votes = self.votes - 1            print(Thread.current.description + "  余票数为 " + self.votes.description)        }        else {            print(Thread.current.description + "  无票")            break;        }    }}

OC

// 票数@property (nonatomic, assign) int votes;- (void)viewDidLoad {    [super viewDidLoad];    self.tickets = 10;    // 售票    [self saleTickets];}- (void)saleTickets{    while(YES)    {        // 模拟耗时操作        [NSThread sleepForTimeInterval:1.0];        if(self.votes > 0)        {            self.votes--;            NSLog(@"余票数为%d", self.votes);        }        else        {            NSLog(@"无票");            break;        }    }}

问题

这时候我们在单线程上面运行是没问题的, 但是当有多个售票员(子线程)在卖票(共同访问同一个数据)的时候, 就会出现以下的情况:
代码:
OC

// 售票员1NSThread *conductor1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];conductor1.name = @"售票员1";[conductor1 start];// 售票员2NSThread *conductor2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];conductor2.name = @"售票员2";[conductor2 start];

Swift

// 售票员1let conductor1 = Thread.init(target: self, selector: #selector(saleTickets), object: nil)conductor1.name = "售票员1"conductor1.start()// 售票员2let conductor2 = Thread.init(target: self, selector: #selector(saleTickets), object: nil)conductor2.name = "售票员2"conductor2.start()

打印:
这里写图片描述

PS: 笔者记得两三年前刚接触iOS的多线程的时候, 测试有次测得余票数是-1的, 现在侧很久都没发现有-1的情况. 有兴趣的大兄弟可以试试

原因

这里写图片描述

解决

在共同资源( 票数 )被访问( 确切说是准备变化 )的时候( 如上图线程1写入时 ), 该资源只能被一个线程操作.
这时, 我们就可以使用互斥锁等保证被锁定的代码( 资源 ), 同一时间, 只能有一个线程可以操作.

代码修改:
Swift

@objc func saleTickets() {    while true {        Thread.sleep(forTimeInterval: 0.1)        /// 添加互斥锁        /// 由于互斥锁为了数据安全会牺牲性能, 所有互斥锁的范围一定要尽可能地小        /// 参数: 可以使任意的继承自基类NSObject的对象        /// 但是, 这个参数一定要确保, 互斥的其他线程认识这把锁.        /// 由于self本身就是一个全局变量, 所以一般我们都直接给self        objc_sync_enter(self)        if(self.votes > 0) {            self.votes = self.votes - 1            print(Thread.current.description + "  余票数为 " + self.votes.description)        }        else {            print(Thread.current.description + "  无票, 当前票数为" + self.votes.description)            break;        }        objc_sync_exit(self) // 记得退出互斥锁    }}

OC

- (void)saleTickets{    while(YES)    {        // 模拟耗时操作        [NSThread sleepForTimeInterval:1.0];        // 添加互斥锁        @synchronized (self)         {            if(self.votes > 0)            {                self.votes--;                NSLog(@"余票数为%d", self.votes);            }            else            {                NSLog(@"无票");                break;            }        }    }}