iOS多線程下的數(shù)據(jù)安全

在多線程操作過程中,往往一個(gè)數(shù)據(jù)同時(shí)被多個(gè)線程讀寫,在這種情況下,如果沒有相應(yīng)的機(jī)制對(duì)數(shù)據(jù)進(jìn)行保護(hù),就很可能會(huì)發(fā)生數(shù)據(jù)污染的的問題,給程序造成各種難以重現(xiàn)的潛在bug.

多線程的安全隱患

下面是一個(gè)模擬的會(huì)導(dǎo)致奔潰的程序代碼

- (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];
}

用戶在點(diǎn)擊start按鈕后,會(huì)在一個(gè)全局的queue里面對(duì)構(gòu)造的數(shù)據(jù)進(jìn)行遍歷,為了模擬實(shí)際場(chǎng)景中網(wǎng)絡(luò)請(qǐng)求的時(shí)延,每次循環(huán)讓當(dāng)前線程休息0.05s,這樣在遍歷的過程中,如果用戶點(diǎn)擊了移除按鈕,此時(shí)self.dataSource[i]執(zhí)行時(shí),因?yàn)閿?shù)組已經(jīng)被清空了,會(huì)報(bào)數(shù)組越界的錯(cuò)誤。

如何解決

在多線程操作過程中,如何保護(hù)共享數(shù)據(jù),其實(shí)已經(jīng)是一個(gè)眾所周知的事情了,這里總結(jié)下自己試過的處理方法:

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

下面是使用@synchronized修復(fù)的示例:

- (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修復(fù)的示例:

 //聲明一個(gè)全局變量
 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修復(fù)的示例:

//聲明全局變量
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修復(fù)的示例:

//聲明全局變量
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];
    });
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容