基于pthread_mutex封装的锁NSLock、NSCondition、NSConditionLock、NSRecursiveLock、@Synchronized

参考链接:iOS多线程中的几种加锁类型OSSpinLock、os_unfair_lock、pthread_mutex

1、pthread_mutex是c语言实现的跨平台互斥锁,应该是一种主流锁吧,OC多种形态的锁都是基于pthread_mutex,常见的有NSLock、NSCondition、NSConditionLock、NSRecursiveLock、@Synchronized

2、各种锁的基本使用
2.1、NSLock

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
#import "ViewController.h"
#import <pthread .h>
 
@interface ViewController ()
{
    NSLock           *_lock;
}
@end
 
@implementation ViewController
static NSUInteger ticketCount = 20;
- (void)viewDidLoad
{
    [super viewDidLoad];
    _lock = [[NSLock alloc] init];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 5; i++) {
            [self saleTickte];
        }
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 5; i++) {
            [self saleTickte];
        }
    });
}
 
- (void)saleTickte
{
    [_lock lock];
    // lockBeforeDate:方法能在指定时间后自动解锁
    // [_lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:3]];
    NSUInteger remain = ticketCount;
    sleep(0.5);
    ticketCount = --remain;
    NSLog(@"%ld-%@", ticketCount, [NSThread currentThread]);
    [_lock unlock];
}
@end

输出结果:

2018-11-27 16:38:35.510970+0800 MultiThread[5033:2888041] 19-<NSThread: 0x600001ded000>{number = 3, name = (null)}
2018-11-27 16:38:35.511540+0800 MultiThread[5033:2888038] 18-<NSThread: 0x600001de0780>{number = 4, name = (null)}
2018-11-27 16:38:35.512453+0800 MultiThread[5033:2888038] 17-<NSThread: 0x600001de0780>{number = 4, name = (null)}
2018-11-27 16:38:35.513072+0800 MultiThread[5033:2888038] 16-<NSThread: 0x600001de0780>{number = 4, name = (null)}
2018-11-27 16:38:35.513250+0800 MultiThread[5033:2888041] 15-<NSThread: 0x600001ded000>{number = 3, name = (null)}
2018-11-27 16:38:35.513405+0800 MultiThread[5033:2888041] 14-<NSThread: 0x600001ded000>{number = 3, name = (null)}
2018-11-27 16:38:35.513543+0800 MultiThread[5033:2888041] 13-<NSThread: 0x600001ded000>{number = 3, name = (null)}
2018-11-27 16:38:35.513699+0800 MultiThread[5033:2888041] 12-<NSThread: 0x600001ded000>{number = 3, name = (null)}
2018-11-27 16:38:35.513880+0800 MultiThread[5033:2888038] 11-<NSThread: 0x600001de0780>{number = 4, name = (null)}
2018-11-27 16:38:35.514671+0800 MultiThread[5033:2888038] 10-<NSThread: 0x600001de0780>{number = 4, name = (null)}

PS:使用pthread_mutex加锁演示上述代码结果是某个线程先加锁,似乎当前线程循环结束后才会有其他线程来抢占;但NSLock的结果即是for循环随机交替进行的。
2.2、NSCondition

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
#import "ViewController.h"
#import <pthread.h>
 
@interface ViewController ()
{
    NSCondition           *_condition;
}
@end
 
@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    _condition = [[NSCondition alloc] init];
 
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self methodOne];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self methodTwo];
    });
}
- (void)methodOne
{
    [_condition lock];
    [_condition wait];
    // waitUntilDate:方法在等待时间之后被主动唤醒
    // [_condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
    NSLog(@"one, %@", [NSThread currentThread]);
    [_condition unlock];
}
- (void)methodTwo
{
    // 休眠0.5秒是为了让其他线程先抢占加锁
    sleep(0.5);
    [_condition lock];
    NSLog(@"two, %@", [NSThread currentThread]);
    [_condition signal];
    [_condition unlock];
}
@end

执行结果:

2018-11-27 16:46:13.041122+0800 MultiThread[5168:2952222] two, <NSThread: 0x60000348a200>{number = 3, name = (null)}
2018-11-27 16:46:13.043408+0800 MultiThread[5168:2952220] one, <NSThread: 0x600003493100>{number = 4, name = (null)}

2.3、NSConditionLock

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 ()
{
    NSConditionLock           *_conditionLock;
}
@end
 
@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    _conditionLock = [[NSConditionLock alloc] initWithCondition:0];
 
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self methodOne];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self methodTwo];
    });
}
- (void)methodOne
{
    [_conditionLock lockWhenCondition:0];
    // lock方法等价于lockWhenCondition:0
    // [_conditionLock lock];
    // lockBeforeDate:、lockWhenCondition:beforeDate:方法同理
    // [_conditionLock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:3]];
    // [_conditionLock lockWhenCondition:0 beforeDate:[NSDate dateWithTimeIntervalSinceNow:3]];
    NSLog(@"one, %@", [NSThread currentThread]);
    sleep(1); 
    // 解锁当前锁,并发送condition信号给其他条件锁
    [_conditionLock unlockWithCondition:1];
}
- (void)methodTwo
{
    // 在接收到conditon之前处于休眠状态
    // lockWhenCondiont:beforeDate:方法可以避免死锁
    [_conditionLock lockWhenCondition:1];
    NSLog(@"two, %@", [NSThread currentThread]);
    [_conditionLock unlockWithCondition:1];
}
@end

运行结果:

2018-11-27 16:59:42.482479+0800 MultiThread[5378:3062761] one, <NSThread: 0x60000009a140>{number = 3, name = (null)}
2018-11-27 16:59:43.484827+0800 MultiThread[5378:3062762] two, <NSThread: 0x60000009ac40>{number = 4, name = (null)}

2.4、NSRecursiveLock

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
#import "ViewController.h"
#import <pthread.h>
 
@interface ViewController ()
{
    NSRecursiveLock           *_recursiveLock;
}
@end
 
@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    // 等价于pthread_mutexattr设置type PTHREAD_MUTEX_RECURSIVE
    _recursiveLock = [[NSRecursiveLock alloc] init];
 
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self recursiveMethod];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self recursiveMethod];
    });
}
- (void)recursiveMethod
{
    static int i = 0;
    [_recursiveLock lock];
    if (i < 5) {
        i++;
        sleep(1);// 休眠1秒可以看出,当前线程占用锁后,其他线程无法再次加锁,但当前线程可以
        NSLog(@"%d, %@", i, [NSThread currentThread]);
        [self recursiveMethod];
    }
    [_recursiveLock unlock];
    i = 0;
}
@end

运行结果:

2018-11-27 17:08:30.437419+0800 MultiThread[5575:3146772] 1, <NSThread: 0x60000246f940>{number = 3, name = (null)}
2018-11-27 17:08:31.441390+0800 MultiThread[5575:3146772] 2, <NSThread: 0x60000246f940>{number = 3, name = (null)}
2018-11-27 17:08:32.444975+0800 MultiThread[5575:3146772] 3, <NSThread: 0x60000246f940>{number = 3, name = (null)}
2018-11-27 17:08:33.448204+0800 MultiThread[5575:3146772] 4, <NSThread: 0x60000246f940>{number = 3, name = (null)}
2018-11-27 17:08:34.451139+0800 MultiThread[5575:3146772] 5, <NSThread: 0x60000246f940>{number = 3, name = (null)}
2018-11-27 17:08:35.454776+0800 MultiThread[5575:3146773] 1, <NSThread: 0x600002474000>{number = 4, name = (null)}
2018-11-27 17:08:36.459969+0800 MultiThread[5575:3146773] 2, <NSThread: 0x600002474000>{number = 4, name = (null)}
2018-11-27 17:08:37.462625+0800 MultiThread[5575:3146773] 3, <NSThread: 0x600002474000>{number = 4, name = (null)}
2018-11-27 17:08:38.466880+0800 MultiThread[5575:3146773] 4, <NSThread: 0x600002474000>{number = 4, name = (null)}
2018-11-27 17:08:39.472285+0800 MultiThread[5575:3146773] 5, <NSThread: 0x600002474000>{number = 4, name = (null)}

2.5、@synchronized(token){}

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
#import "ViewController.h"
#import <pthread.h>
 
@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self methodOne];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self methodTwo];
    });
}
- (void)methodOne
{
    // token相同即为同一锁,大括号开始为加锁,结果为解锁,使用简单但效率不高
    @synchronized (self) {
        NSLog(@"one, %@", [NSThread currentThread]);
        sleep(1);
    }
}
- (void)methodTwo
{
    @synchronized (self) {
        NSLog(@"two, %@", [NSThread currentThread]);
    }
}
@end

执行结果:

2018-11-27 17:15:02.233273+0800 MultiThread[5688:3202460] one, <NSThread: 0x60000312d780>{number = 3, name = (null)}
2018-11-27 17:15:03.237778+0800 MultiThread[5688:3202462] two, <NSThread: 0x60000312ec80>{number = 4, name = (null)}

One thought on “基于pthread_mutex封装的锁NSLock、NSCondition、NSConditionLock、NSRecursiveLock、@Synchronized

  1. Sian Post author

    iOS线程同步方案性能比较:
    os_unfair_lock > OSSpinLock > dispatch_semaphore > pthread_mutex > dispatch_queue > NSLock > NSCondition > pthread_mutex(recursive) > NSRecursiveLock > NSConditionLock > @Synchronized

Leave a Reply