多线程线程资源共享问题

简介

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

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

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

代码

Swift

var votes = 10

override 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

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

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

Swift

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

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

打印:
多线程线程资源共享问题_第1张图片

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

原因

多线程线程资源共享问题_第2张图片

解决

在共同资源( 票数 )被访问( 确切说是准备变化 )的时候( 如上图线程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;
            }
        }
    }
}

你可能感兴趣的:(多线程)