iOS多线程中的几种加锁类型OSSpinLock、os_unfair_lock、pthread_mutex

1、基本介绍
1.1、OSSpinLock
1.1.1、自旋锁,iOS10及以后版本已标记过期;
1.1.2、锁定状态下,其他线程访问时处于忙等状态
1.1.3、存在优先级反转的问题而被放弃;
1.2、os_unfair_lock
1.2.1、互斥锁
1.2.2、锁定状态下,其他线程访问时会处于休眠状态
1.3、pthread_mutex
1.3.1、互斥锁
1.3.2、可跨平台使用

2、代码实现
2.1、OSSpinLock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#import "ViewController.h"
#import <libkern/OSAtomic.h>
#import <os/lock.h>
#import <pthread.h>
 
@interface ViewController ()
{
    NSUInteger      _ticketCount;   // 剩余票数
    OSSpinLock      _spinLock;      // Spin锁(自旋锁)
}
@end
 
@implementation ViewController
 
- (void)viewDidLoad
{
    [super viewDidLoad];
    // 锁初始化
    _spinLock = OS_SPINLOCK_INIT;
    // 同时开启两个异步线程对票数进行自减
    _ticketCount = 20;
    // 创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create("custom-queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}
 
- (void)saleTicket
{
    // 1、线程加锁
    OSSpinLockLock(&_spinLock);
    // 2、执行
    NSUInteger remain = _ticketCount;
    // 2.1、先取数据,休眠1秒后再写入,通过窗口期制造写入冲突
    sleep(1);
    _ticketCount = --remain;
    NSLog(@"%ld, %@", _ticketCount, [NSThread currentThread]);
    // 3、解锁
    OSSpinLockUnlock(&_spinLock);
}

打印输出:
加锁前:

2018-11-26 16:28:44.022292+0800 MultiThread[8426:1133657] 19, <NSThread: 0x600000290400>{number = 3, name = (null)}
2018-11-26 16:28:44.022322+0800 MultiThread[8426:1133656] 19, <NSThread: 0x60000029d0c0>{number = 4, name = (null)}
2018-11-26 16:28:44.022581+0800 MultiThread[8426:1133657] 18, <NSThread: 0x600000290400>{number = 3, name = (null)}
2018-11-26 16:28:44.022625+0800 MultiThread[8426:1133656] 18, <NSThread: 0x60000029d0c0>{number = 4, name = (null)}
2018-11-26 16:28:44.022749+0800 MultiThread[8426:1133656] 16, <NSThread: 0x60000029d0c0>{number = 4, name = (null)}
2018-11-26 16:28:44.022735+0800 MultiThread[8426:1133657] 17, <NSThread: 0x600000290400>{number = 3, name = (null)}
2018-11-26 16:28:44.022851+0800 MultiThread[8426:1133656] 15, <NSThread: 0x60000029d0c0>{number = 4, name = (null)}
2018-11-26 16:28:44.022860+0800 MultiThread[8426:1133657] 14, <NSThread: 0x600000290400>{number = 3, name = (null)}
2018-11-26 16:28:44.023016+0800 MultiThread[8426:1133657] 13, <NSThread: 0x600000290400>{number = 3, name = (null)}
2018-11-26 16:28:44.024139+0800 MultiThread[8426:1133656] 12, <NSThread: 0x60000029d0c0>{number = 4, name = (null)}

加锁后:

2018-11-26 15:43:17.603606+0800 MultiThread[6545:997594] 19, <NSThread: 0x600002e06e80>{number = 3, name = (null)}
2018-11-26 15:43:17.603914+0800 MultiThread[6545:997594] 18, <NSThread: 0x600002e06e80>{number = 3, name = (null)}
2018-11-26 15:43:17.604286+0800 MultiThread[6545:997594] 17, <NSThread: 0x600002e06e80>{number = 3, name = (null)}
2018-11-26 15:43:17.604444+0800 MultiThread[6545:997594] 16, <NSThread: 0x600002e06e80>{number = 3, name = (null)}
2018-11-26 15:43:17.604581+0800 MultiThread[6545:997594] 15, <NSThread: 0x600002e06e80>{number = 3, name = (null)}
2018-11-26 15:43:17.605013+0800 MultiThread[6545:997592] 14, <NSThread: 0x600002e02cc0>{number = 4, name = (null)}
2018-11-26 15:43:17.605168+0800 MultiThread[6545:997592] 13, <NSThread: 0x600002e02cc0>{number = 4, name = (null)}
2018-11-26 15:43:17.605287+0800 MultiThread[6545:997592] 12, <NSThread: 0x600002e02cc0>{number = 4, name = (null)}
2018-11-26 15:43:17.605406+0800 MultiThread[6545:997592] 11, <NSThread: 0x600002e02cc0>{number = 4, name = (null)}
2018-11-26 15:43:17.605522+0800 MultiThread[6545:997592] 10, <NSThread: 0x600002e02cc0>{number = 4, name = (null)}

2.2、os_unfair_lock、pthread_mutex
2.2.1、os_unfair_lock

1
2
3
4
5
6
7
8
9
// 1、初始化
os_unfair_lock unfair_lock = OS_UNFAIR_LOCK_INIT;
// 2、加锁
os_unfair_lock_lock(&unfair_lock);
 
// coding.....
 
// 3、解锁
os_unfair_lock_unlock(&unfair_lock);

2.2.2、pthread_mutex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1.1、初始化锁属性
pthread_mutexattr_t pthread_mutexattr;
pthread_mutexattr_init(&pthread_mutexattr);
pthread_mutexattr_settype(&pthread_mutexattr, PTHREAD_MUTEX_DEFAULT);
// 递归锁
// pthread_mutexattr_settype(&pthread_mutexattr, PTHREAD_MUTEX_RECURSIVE);
 
// 1.2、初始化锁
pthread_mutex_t pthread_mutex;
pthread_mutex_init(&pthread_mutex, &pthread_mutexattr);
pthread_mutexattr_destroy(&pthread_mutexattr);
// 2、加锁
pthread_mutex_lock(&pthread_mutex);
 
// coding.....
 
// 3、解锁
pthread_mutex_unlock(&pthread_mutex);

3 thoughts on “iOS多线程中的几种加锁类型OSSpinLock、os_unfair_lock、pthread_mutex

  1. Pingback: 基于pthread_mutex封装的锁NSLock、NSCondition、NSConditionLock、NSRecursiveLock、@Synchronized | 一天到晚游泳的余

  2. Sian Post author

    条件锁的基本实现
    1、在特定逻辑条件中添加条件锁,此时会临时解锁;
    2、其他线程可对此锁进行加锁操作;
    3、当接收到唤醒信号时,条件锁会重新加锁执行;
    4、示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    
    #import "ViewController.h"
    #import <pthread.h>
     
    @interface ViewController ()
    {
        pthread_mutex_t     _lock;
        pthread_cond_t      _cond;
    }
    @end
     
    @implementation ViewController
     
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        pthread_mutexattr_t mutexattr;
        pthread_mutexattr_init(&mutexattr);
        pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_DEFAULT);
        pthread_mutex_init(&_lock, &mutexattr);
        pthread_mutexattr_destroy(&mutexattr);
     
        pthread_cond_init(&_cond, NULL);
     
        NSMutableArray *array = [NSMutableArray array];
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self removeObjectForArray:array];
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self addObjectForArray:array];
        });
    }
    - (void)removeObjectForArray:(NSMutableArray *)array
    {
        pthread_mutex_lock(&_lock);
        NSLog(@"start remove object...");
        if (array.count == 0){
            // 条件锁、条件等待,此时会临时解锁,等待条件唤醒会再次加锁
            pthread_cond_wait(&_cond, &_lock);
        }
        [array removeLastObject];
        NSLog(@"ended remove object...");
        pthread_mutex_unlock(&_lock);
    }
    - (void)addObjectForArray:(NSMutableArray *)array
    {
        sleep(3.0); // 延时3秒
        pthread_mutex_lock(&_lock);
        NSLog(@"start add object...");
        [array addObject:@"object"];
        NSLog(@"ended add object...");
        // 条件信号,针对特定条件发布信号,唤醒条件休眠锁,本次解锁后条件锁唤醒执行
        pthread_cond_signal(&_cond);
        NSLog(@"pthread_cond_signal");
        pthread_mutex_unlock(&_lock);
        NSLog(@"pthread_mutex_unlock");
    }
    @end

    5、打印结果:

    2018-11-26 17:23:58.597108+0800 MultiThread[9263:1278519] start remove object...
    2018-11-26 17:24:01.602201+0800 MultiThread[9263:1278518] start add object...
    2018-11-26 17:24:01.602440+0800 MultiThread[9263:1278518] ended add object...
    2018-11-26 17:24:01.602707+0800 MultiThread[9263:1278518] pthread_cond_signal
    2018-11-26 17:24:01.602925+0800 MultiThread[9263:1278518] pthread_mutex_unlock
    2018-11-26 17:24:01.602992+0800 MultiThread[9263:1278519] ended remove object...
  3. Sian Post author

    递归锁的简单实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    #import "ViewController.h"
    #import <pthread.h>
     
    @interface ViewController ()
    {
        pthread_mutex_t     _lock;
    }
    @end
     
    @implementation ViewController
     
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // 1、初始化锁属性
        pthread_mutexattr_t mutexattr;
        pthread_mutexattr_init(&mutexattr);
        pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
        // 2、初始化锁
        pthread_mutex_init(&_lock, &mutexattr);
        pthread_mutexattr_destroy(&mutexattr);
     
        [self recursiveMethod];
    }
     
    /* 1、pthread_mutexattr_t中type如果为PTHREAD_MUTEX_DEFAULT则递归调用会被休眠
     * 因为当前位置已被加锁,而解锁在递归调用之后,所以会造成死锁;
     * 最终运行结果只能看到一次打印,线程处于休眠状态
     * 2、pthread_mutexattr_t中type如果为PTHREAD_MUTEX_RECURSIVE可以解决递归问题
     * 如果是递归锁,同一线程是可以被重复加锁,继续往下执行
     * 一次回归会解一次锁,当解锁次数与加锁次数相等时锁被完全解除,其他线程也可调用此处;
     */
    - (void)recursiveMethod
    {
        pthread_mutex_lock(&_lock);
        static int num = 0;
        if (num < 10){
            printf("%dn", num++);
            [self recursiveMethod];
        }
        pthread_mutex_unlock(&_lock);
    }
     
    @end

Leave a Reply