iOS多线程下的数据安全

在多线程操作过程中,往往一个数据同时被多个线程读写,在这种情况下,如果没有相应的机制对数据进行保护,就很可能会发生数据污染的的问题,给程序造成各种难以重现的潜在bug.

多线程的安全隐患

下面是一个模拟的会导致奔溃的程序代码

- (void)viewDidLoad
{
    [self configData];
}

- (void)configData
{
    self.dataSource = [NSMutableArray array];
    for (int i = 0; i < 100; i++) {
        [self.dataSource addObject:[NSString stringWithFormat:@"Obj - %i", i]];
    }
}

- (IBAction)start:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0; i < self.dataSource.count; i++) {
            [NSThread sleepForTimeInterval:0.05];
            NSLog(@"%@", self.dataSource[i]);
        }
    });
}

- (IBAction)removeAllObjs:(id)sender
{
    [self.dataSource removeAllObjects];
}

用户在点击start按钮后,会在一个全局的queue里面对构造的数据进行遍历,为了模拟实际场景中网络请求的时延,每次循环让当前线程休息0.05s,这样在遍历的过程中,如果用户点击了移除按钮,此时self.dataSource[i]执行时,因为数组已经被清空了,会报数组越界的错误。

如何解决

在多线程操作过程中,如何保护共享数据,其实已经是一个众所周知的事情了,这里总结下自己试过的处理方法:

  1. @synchronized
  2. NSLock
  3. dispatch_semaphore_signal
  4. dispatch_barrier_async

下面是使用@synchronized修复的示例:

- (IBAction)start:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @synchronized(self.dataSource) {
            for (int i = 0; i < self.dataSource.count; i++) {
                [NSThread sleepForTimeInterval:0.05];
                NSLog(@"%@",self.dataSource[i]);
            }
        }
    });
}

- (IBAction)removeAllObjs:(id)sender
{
    @synchronized(self.dataSource) {
        [self.dataSource removeAllObjects];
    }
}

下面是使用NSLock修复的示例:

 //声明一个全局变量
 NSRecursiveLock* rLock = [[NSRecursiveLock alloc] init];
 - (IBAction)start:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [rLock lock];
        for (int i = 0; i < self.dataSource.count; i++) {
            [NSThread sleepForTimeInterval:0.05];
            NSLog(@"%@", self.dataSource[i]);
        }
        [rLock unlock];
    });
}

- (IBAction)removeAllObjs:(id)sender
{
    [rLock lock];
    [self.dataSource removeAllObjects];
    [rLock unlock];
}

下面是使用dispatch_semaphore_signal修复的示例:

//声明全局变量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

- (IBAction)start:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        for (int i = 0; i < self.dataSource.count; i++) {
            [NSThread sleepForTimeInterval:0.05];
            NSLog(@"%@",self.dataSource[i]);
        }
        dispatch_semaphore_signal(semaphore);
    });

}

- (IBAction)removeAllObjs:(id)sender
{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [self.dataSource removeAllObjects];
    dispatch_semaphore_signal(semaphore);
}

下面是使用dispatch_barrier_async修复的示例:

//声明全局变量
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.threadsafe.sing", DISPATCH_QUEUE_CONCURRENT);

- (IBAction)start:(id)sender
{
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < self.dataSource.count; i++) {
            [NSThread sleepForTimeInterval:0.05];
            NSLog(@"%@", self.dataSource[i]);
        }
    });
}

- (IBAction)removeAllObjs:(id)sender
{
    dispatch_barrier_async(concurrentQueue, ^{
        [self.dataSource removeAllObjects];
    });
}

你可能感兴趣的:(iOS多线程下的数据安全)