iOS多线程下的数据安全

作者: 仰望那片天空 | 来源:发表于2016-02-03 17:03 被阅读1045次

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

        本文链接:https://www.haomeiwen.com/subject/epdekttx.html