美文网首页DevSupportiOS开发 技术集锦
线程安全的NSMutableDictionary

线程安全的NSMutableDictionary

作者: iOS104 | 来源:发表于2017-06-09 12:00 被阅读332次

NSDictionary是线程安全的,NSMutableDictionary是线程不安全的。
利用锁来保证线程的安全。

代码地址,欢迎star

测试方式:

  • 对NSMutableDictionary进行100000次set和get操作。
  • 设备为iPhone 6

现在有几种方式可以保证线程安全

  • @synchronized
  • NSLock
  • OSSpinLock
  • GCD semaphore
  • GCD serial queue
  • pthread mutex

在子线程串行执行结果:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        double then, now;
        
        then = CFAbsoluteTimeGetCurrent();
        for (NSUInteger i = 0; i < _iterationsCount; i++)
        {
            [_syncDictionary setObject:@(i) forKey:@(i)];
            [_syncDictionary objectForKey:@(i)];
        }
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.synchronizedLabel.text = [NSString stringWithFormat:@"@synchronized: %f sec\n", now-then];
        });
        
        
        then = CFAbsoluteTimeGetCurrent();
        for (NSUInteger i = 0; i < _iterationsCount; i++)
        {
            [_nsLockDictionary setObject:@(i) forKey:@(i)];
            [_nsLockDictionary objectForKey:@(i)];
        }
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.nsLockLabel.text = [NSString stringWithFormat:@"NSLock: %f sec\n", now-then];
        });
        
        then = CFAbsoluteTimeGetCurrent();
        for (NSUInteger i = 0; i < _iterationsCount; i++)
        {
            [_spinLockDictionary setObject:@(i) forKey:@(i)];
            [_spinLockDictionary objectForKey:@(i)];
        }
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.osSpinLockLabel.text = [NSString stringWithFormat:@"OSSpinLock: %f sec\n", now-then];
        });
        
        then = CFAbsoluteTimeGetCurrent();
        for (NSUInteger i = 0; i < _iterationsCount; i++)
        {
            [_semaphoreDictionary setObject:@(i) forKey:@(i)];
            [_semaphoreDictionary objectForKey:@(i)];
        }
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.gcdSemaphoreLabel.text = [NSString stringWithFormat:@"GCD Semaphore: %f sec\n", now-then];
        });
        
        then = CFAbsoluteTimeGetCurrent();
        for (NSUInteger i = 0; i < _iterationsCount; i++)
        {
            [_queueDictionary setObject:@(i) forKey:@(i)];
            [_queueDictionary objectForKey:@(i)];
        }
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.gcdQueueLabel.text = [NSString stringWithFormat:@"GCD Queue: %f sec\n", now-then];
        });
        
        then = CFAbsoluteTimeGetCurrent();
        for (NSUInteger i = 0; i < _iterationsCount; i++)
        {
            [_ptMutexDictionary setObject:@(i) forKey:@(i)];
            [_ptMutexDictionary objectForKey:@(i)];
        }
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.pthreadMutexLabel.text = [NSString stringWithFormat:@"PThread mutex: %f sec\n", now-then];
            
            self.twoBlockTestButton.enabled = YES;
            self.lockUnlockTestButton.enabled = YES;
        });
        
    });
image.png

线程安全的NSMutableDictionary,在两个并发的block中执行100000次set,get

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        double then, now;
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        
        then = CFAbsoluteTimeGetCurrent();
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_syncDictionary setObject:@(i) forKey:@(i)];
                [_syncDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_syncDictionary setObject:@(i) forKey:@(i)];
                [_syncDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.synchronizedLabel.text = [NSString stringWithFormat:@"@synchronized: %f sec\n", now-then];
        });
        
        
        then = CFAbsoluteTimeGetCurrent();
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_nsLockDictionary setObject:@(i) forKey:@(i)];
                [_nsLockDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_nsLockDictionary setObject:@(i) forKey:@(i)];
                [_nsLockDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.nsLockLabel.text = [NSString stringWithFormat:@"NSLock: %f sec\n", now-then];
        });
        
        then = CFAbsoluteTimeGetCurrent();
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_spinLockDictionary setObject:@(i) forKey:@(i)];
                [_spinLockDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_spinLockDictionary setObject:@(i) forKey:@(i)];
                [_spinLockDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.osSpinLockLabel.text = [NSString stringWithFormat:@"OSSpinLock: %f sec\n", now-then];
        });
        
        then = CFAbsoluteTimeGetCurrent();
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_semaphoreDictionary setObject:@(i) forKey:@(i)];
                [_semaphoreDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_semaphoreDictionary setObject:@(i) forKey:@(i)];
                [_semaphoreDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.gcdSemaphoreLabel.text = [NSString stringWithFormat:@"GCD Semaphore: %f sec\n", now-then];
        });
        
        then = CFAbsoluteTimeGetCurrent();
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_queueDictionary setObject:@(i) forKey:@(i)];
                [_queueDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_queueDictionary setObject:@(i) forKey:@(i)];
                [_queueDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.gcdQueueLabel.text = [NSString stringWithFormat:@"GCD Queue: %f sec\n", now-then];
        });
        
        then = CFAbsoluteTimeGetCurrent();
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_ptMutexDictionary setObject:@(i) forKey:@(i)];
                [_ptMutexDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (NSUInteger i = 0; i < _iterationsCount; i++)
            {
                [_ptMutexDictionary setObject:@(i) forKey:@(i)];
                [_ptMutexDictionary objectForKey:@(i)];
            }
            
            dispatch_semaphore_signal(semaphore);
        });
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        now = CFAbsoluteTimeGetCurrent();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.pthreadMutexLabel.text = [NSString stringWithFormat:@"PThread mutex: %f sec\n", now-then];
            
            self.twoBlockTestButton.enabled = YES;
            self.lockUnlockTestButton.enabled = YES;
        });
    });
image.png

在并发的线程安全的NSMutableDictionary,性能最好的是OSSpinLock。

自旋锁的优点:

  • 自旋锁,和互斥锁类似,都是为了保证线程安全的锁。但二者的区别是不一样的,对于互斥锁,当一个线程获得这个锁之后,其他想要获得此锁的线程将会被阻塞,直到该锁被释放。
  • 但自选锁不一样,当一个线程获得锁之后,其他线程将会一直循环在哪里查看是否该锁被释放。所以,此锁比较适用于锁的持有者保存时间较短的情况下。

但是OSSpinLock不安全。

  • 新版 iOS 中,系统维护了 5 个不同的线程优先级/QoS: background,utility,default,user-initiated,user-interactive。高优先级线程始终会在低优先级线程前执行,一个线程不会受到比它更低优先级线程的干扰。这种线程调度算法会产生潜在的优先级反转问题,从而破坏了 spin lock。

  • 具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。这并不只是理论上的问题,libobjc 已经遇到了很多次这个问题了,于是苹果的工程师停用了 OSSpinLock。

os_unfair_lock

自旋锁已经不在安全,然后苹果又整出来个 os_unfair_lock_t
这个锁解决了优先级反转问题。

    os_unfair_lock_t unfairLock;
    unfairLock = &(OS_UNFAIR_LOCK_INIT);
    os_unfair_lock_lock(unfairLock);
    os_unfair_lock_unlock(unfairLock);

结论

所以如果同一线程优先级可以用,OSSpinLock。如果不在同一线程优先级的话在iOS 10用os_unfair_lock

相关文章

网友评论

    本文标题:线程安全的NSMutableDictionary

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