iOS 多线程安全高效的访问数据

在 上篇 博客我们讲到 多线程访问数据时会出现数据不正常的问题。现在我们来讲如何安全而且高效的访问数据。

方案一: 

我们使用 @synchronized 方式 来解决 假设在 controller 中 有如下的属性iOS 多线程安全高效的访问数据_第1张图片iOS 多线程安全高效的访问数据_第2张图片iOS 多线程安全高效的访问数据_第3张图片

@property (nonatomic,assign)NSInteger synchronizedTickets;


- (void)syncronized

{

    self.synchronizedTickets=10;

    _queueTickets=10;

    count=10;

    

    //开启多个线程,模拟售票员售票

    self.thread10=[[NSThread alloc]initWithTarget:self selector:@selector(sycronizedSell) object:nil];

    self.thread10.name=@"售票员10";

    

    self.thread11=[[NSThread alloc]initWithTarget:self selector:@selector(sycronizedSell) object:nil];

    self.thread11.name=@"售票员11";

    

    self.thread12=[[NSThread alloc]initWithTarget:self selector:@selector(sycronizedSell) object:nil];

    self.thread12.name=@"售票员12";

    

    [self.thread10 start];

    [self.thread11 start];

    [self.thread12 start];

}


在 sycronizedSell  中 我们这样写

while (true) {

 // 从代码来看应该 应该不可能出现 负数的 票数

 //        NSLog(@"start ticket %ld---",(long)self.leftTicketsCount);

 @synchronized (self) {

 if (self.synchronizedTickets>0) { // 读取

 

 

 // NSLog(@"valid ticke %ld",self.newLefTicketsCount);

 [NSThread sleepForTimeInterval:1]; // 这句话 是关键 睡眠 执行 笔者认为 如果

 self.synchronizedTickets--;

 NSLog(@"synchronizedTickets  thread:%@ ----> %ld",[[NSThread currentThread] name],self.synchronizedTickets);

 

 

 

 }else{

 break;

 }

 }

iOS 多线程安全高效的访问数据_第4张图片

 

第二中 方案 使用 NSLock 

NSLock *_lock; 是一个全局的外部变量

更改 sycronizedSell 方法为 

while (true) {

 // 从代码来看应该 应该不可能出现 负数的 票数

 //        NSLog(@"start ticket %ld---",(long)self.leftTicketsCount);

 [_lock lock];

 if (self.synchronizedTickets>0) { // 读取

 

 

 // NSLog(@"valid ticke %ld",self.newLefTicketsCount);

 [NSThread sleepForTimeInterval:1]; // 这句话 是关键 睡眠 执行 笔者认为 如果

 self.synchronizedTickets--;

 NSLog(@"synchronizedTickets  thread:%@ ----> %ld",[[NSThread currentThread] name],self.synchronizedTickets);

 

 

 

 }else{

 break;

 }

 

 [_lock unlock];

 }

运行效果如下:

iOS 多线程安全高效的访问数据_第5张图片


第三种 使用 GCD 

思路是: 使用一个并行队列 多线程设置的时候  其他读取的操作都停止。读取的时候要等待上一个读取完

  __block NSInteger count;  // 串行队列

    dispatch_queue_t _syncQueue;

  _asyncQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);


都是外部的变量。



- (NSInteger)queueTickets

{

    __block NSInteger tickets = 0;

    dispatch_sync(_asyncQueue, ^{

        tickets=_queueTickets;

    });

    return tickets;

    

}

- (void)setQueueTickets:(NSInteger)queueTickets

{

    //

    dispatch_barrier_async(_asyncQueue, ^{

        _queueTickets=queueTickets;

    });

}

更改 sycronizedSell  while 循环里面的 代码为 

 if ([self queueTickets]>0) { // 读取

 

 

 // NSLog(@"valid ticke %ld",self.newLefTicketsCount);

 [NSThread sleepForTimeInterval:1]; // 这句话 是关键 睡眠 执行 笔者认为 如果

 [self setQueueTickets:([self queueTickets]-1)];

 

 NSLog(@"synchronizedTickets  thread:%@ ----> %ld",[[NSThread currentThread] name],[self queueTickets]);

 

 

 

 }else{

 break;

 }




这里使用到了GCD 中线程栅栏的功能。

iOS 多线程安全高效的访问数据_第6张图片


我们发现 时间上面比起 上面的这两种方法都要快 而且没有出现0 的情况推荐使用。

第四种 方案 : 使用 串行队列

 __block NSInteger count;  // 串行队列  外部的全局变量 

    dispatch_queue_t _syncQueue;

    _syncQueue=dispatch_queue_create("syncQueue", DISPATCH_QUEUE_SERIAL);

- (void)setCount:(NSInteger)newcount{

    dispatch_sync(_syncQueue, ^{

        count = newcount;

    });

}

- (NSInteger)count{

    __block NSInteger localCount;

    dispatch_sync(_syncQueue, ^{

        localCount = count;

    });

    return localCount;

}


替换 while 循环为

if (count>0) { // 读取

 

 // NSLog(@"valid ticke %ld",self.newLefTicketsCount);

 [NSThread sleepForTimeInterval:1]; // 这句话 是关键 睡眠 执行 笔者认为 如果

 [self setCount:([self count]-1)];

 

 NSLog(@"synchronizedTickets  thread:%@ ----> %ld",[[NSThread currentThread] name],[self count]);

 }else{

 break;

 }


效果如下:

iOS 多线程安全高效的访问数据_第7张图片


我们 发现 还是出现了 负数的票数 。你知道 这是什么原因吗? 欢迎评论








你可能感兴趣的:(iOS,Block,与,GCD)