美文网首页
各种锁介绍以及性能对比

各种锁介绍以及性能对比

作者: 哈豊玛奥 | 来源:发表于2019-02-22 16:55 被阅读0次
  • 多线程为我们带来了很大便利,也提高了程序的执行效率,但同时也带来了Data race(当至少有两个线程同时访问同一个变量,而且至少其中有一个是写操作时,就发生了Data race)。这时就要利用一些同步机制来确保数据的准确性,就是同步机制中的一种。

一、各种锁

  • @synchronized 关键字加锁
  • NSLock 对象锁
  • NSCondition 条件锁1
  • NSConditionLock 条件锁2
  • NSRecursiveLock 递归锁
  • pthread_mutex 互斥锁(C语言)
  • pthread_mutex(recursive) 互斥锁2(递归锁一种类似NSConditionLock)
  • dispath_semaphore 信号量实现加锁(GCD)
  • OSSpinlock 自旋锁(iOS10以后被废弃,因其不安全,有可能造成死锁)
  • os_unfair_lock 自旋锁(iOS之后才可以使用,代替 OSSpinlock的方案)

二、性能比对图

lock_benchmark.png

性能测试Demo的GitHub地址:https://github.com/Yjunjie/MultithreadingAndLock/tree/master
参考链接:https://www.jianshu.com/p/c9c5bc68449d

  • 总体来说:
    OSSpinLockdispatch_semaphore的效率远远高于其他。
    @synchronizedNSConditionLock效率较差。

  • 临界区指的是一块对公共资源进行访问的代码,并非一种机制或是算法。

三、详细介绍

  • 自旋锁:
    如果共享数据被其他线程加锁,那么当前线程会以死循环的方式等待解锁,一旦访问的资源被解锁,则等待线程就会立即执行。自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。

1、OSSpinLock(不安全,已废弃)
可能造成死锁的原因:
有可能在优先级比较低的线程里对共享资源进行加锁了,然后高优先级的线程抢占了低优先级的调用CPU时间,导致高优先级的线程一直在等待低优先级的线程释放锁,然而低优先级根本没法抢占高优先级的CPU时间。
这种情况我们称作 优先级倒转。

OSSpinLock lock = OS_SPINLOCK_INIT;
OSSpinLockLock(&lock);
...
OSSpinLockUnlock(&lock);

2、os_unfair_lock
os_unfair_lock 是苹果官方推荐的替换OSSpinLock的方案,但是它在iOS10.0以上的系统才可以调用。

os_unfair_lock_t unfairLock;
unfairLock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(unfairLock);
os_unfair_lock_unlock(unfairLock);
  • 互斥锁(Mutex):
    如果共享资源已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁,则等待资源的线程会被唤醒。
    互斥锁不会同时被两个不同的线程同时得到。也就是如果是当前线程加的锁,别的线程是没有办法获取这个锁,也就没有办法对他进行解锁。

1、NSLock
Foundation框架中以对象形式暴露给开发者的一种锁,在AFNetworking的AFURLSessionManager.m中应用如下:

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    ...
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
    ...
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    ...
    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

2、pthread_mutex
实际项目中: 在YYKit的YYMemoryCach中可以看到

- (instancetype)init {
    ...
    pthread_mutex_init(&_lock, NULL);
    ...
}
- (void)_trimToCost:(NSUInteger)costLimit {
    BOOL finish = NO;
    pthread_mutex_lock(&_lock);
    if (costLimit == 0) {
        [_lru removeAll];
        finish = YES;
    } else if (_lru->_totalCost <= costLimit) {
        finish = YES;
    }
    pthread_mutex_unlock(&_lock);
    if (finish) return;

    NSMutableArray *holder = [NSMutableArray new];
    while (!finish) {
        if (pthread_mutex_trylock(&_lock) == 0) {
            if (_lru->_totalCost > costLimit) {
                _YYLinkedMapNode *node = [_lru removeTailNode];
                if (node) [holder addObject:node];
            } else {
                finish = YES;
            }
            pthread_mutex_unlock(&_lock);
        } else {
            usleep(10 * 1000); //10 ms
        }
    }
   ...
}

3、@synchronized:
实际项目中:AFNetworking中 isNetworkActivityOccurring属性的getter方法

- (BOOL)isNetworkActivityOccurring {
    @synchronized(self) {
        return self.activityCount > 0;
    }
}
  • 条件锁:
    当进程的某些资源要求不满足时就进入休眠等待,也就是锁住了。直到满足条件后,条件锁打开,进程继续运行。

1、NSCondition

@interface NSCondition : NSObject <NSLocking> {
@private
    void *_priv;
}

- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;

遵循NSLocking协议,使用的时候同样是lock,unlock加解锁,wait是傻等,waitUntilDate:方法是等一会,都会阻塞线程,signal是唤起一个在等待的线程,broadcast是广播全部唤起。

NSCondition *lock = [[NSCondition alloc] init];
//Son 线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    while (No Money) {
        [lock wait];
    }
    NSLog(@"The money has been used up.");
    [lock unlock];
});

 //Father线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    NSLog(@"Work hard to make money.");
    [lock signal];
    [lock unlock];
 });

2、NSConditionLock

@interface NSConditionLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;

@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
  • 递归锁
    特点:同一个线程可以加锁N次而不会死锁。

1、NSRecursiveLock:
NSRecursiveLock在YYKit中YYWebImageOperation.m中有用到:

_lock = [NSRecursiveLock new];
- (void)dealloc {
    [_lock lock];
    ...
    ...
    [_lock unlock];
}

2、pthread_mutex(recursive)
pthread_mutex锁也支持递归,只需要设置PTHREAD_MUTEX_RECURSIVE即可

pthread_mutex_t lock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&lock, &attr);
pthread_mutexattr_destroy(&attr);
pthread_mutex_lock(&lock);
pthread_mutex_unlock(&lock);
  • 信号量加锁
    多元信号量允许多个线程访问同一个资源,多元信号量简称信号量(Semaphore),对于允许多个线程并发访问的资源,这是一个很好的选择。一个初始值为N的信号量允许N个线程并发访问。其实严格的来说信号量不能算锁。而且如果信号量设置为1,我们可以把它当作互斥锁来用

1、dispatch_semaphore
dispatch_semaphore在YYKit中的YYThreadSafeArray.m有所应用,YY大神有这样一句注释:

@discussion Generally, access performance is lower than NSMutableArray, 
 but higher than using @synchronized, NSLock, or pthread_mutex_t.
#define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \
__VA_ARGS__; \
dispatch_semaphore_signal(_lock);

补充:读写锁(又称共享-互斥锁)

允许多个线程同时对同一个数据进行读操作,而只允许一个线程进行写操作。这是因为读操作不会改变数据的内容,是安全的;而写操作会改变数据的内容,是不安全的。

1、pthread_rwlock_t

//加读锁
pthread_rwlock_rdlock(&rwlock);
//解锁
pthread_rwlock_unlock(&rwlock);
//加写锁
pthread_rwlock_wrlock(&rwlock);
//解锁
pthread_rwlock_unlock(&rwlock);

相关文章

  • 各种锁介绍以及性能对比

    多线程为我们带来了很大便利,也提高了程序的执行效率,但同时也带来了Data race(当至少有两个线程同时访问同一...

  • iOS 锁的使用

    参考 demo 参考的文章:iOS开发中的11种锁以及性能对比多线程-线程安全 结论: 自旋锁性能 > 信号量 >...

  • 高并发编程总纲

    个人介绍 Java多线程,各种并发集合,线程安全。 Java锁,性能以及效率。 框架netty。在netty基础上...

  • 2021-03-05

    1.锁(递归锁的实现原理) iOS开发中的11种锁以及性能对比 - 简书[https://www.jianshu....

  • OC-底层原理 26:锁的原理

    本文主要介绍常见的锁,以及synchronized、NSLock、递归锁、条件锁的底层分析 锁 借鉴一张锁的性能数...

  • iOS 锁的原理

    本文主要介绍常见的锁,以及synchronized、NSLock、递归锁、条件锁的底层分析 锁 借鉴一张锁的性能数...

  • iOS-底层原理27:锁的原理

    本文主要介绍常见的锁,以及synchronized、NSLock、递归锁、条件锁的底层分析 锁 借鉴一张锁的性能数...

  • 锁分析(上)

    锁性能分析 iPhone 12真机测试,锁的性能数据对比图 性能从高到低排序:OSSpinLock(自旋锁)>os...

  • iOS中自旋锁与互斥锁的区别

    首先借鉴一张ibireme各种锁性能对比图镇楼 自旋锁与互斥锁的区别 从实现原理上来讲,互斥锁属于sleep-wa...

  • iOS中各种锁的性能对比

    自旋锁 与 互斥锁 自旋锁 (spin lock): 如果一个线程需要获取自旋锁,该锁已经被其他线程占用,该线程不...

网友评论

      本文标题:各种锁介绍以及性能对比

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