多線程-鎖的幾種實(shí)現(xiàn)

主要內(nèi)容

  1. NSLock系
  2. @synchronized
  3. dispatch_semaphore_t

NSLock系

NSLock

NSLock *lock = [NSLock new];
for(NSInteger i = 0; i < 3; i++)
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        NSLog(@"%ld", i);
        [NSThread sleepForTimeInterval:1];
        [lock unlock];
    });
}


結(jié)果(注意時(shí)間)
19:28:12.846 ObjectiveDemo[1146:728046] 0
19:28:13.850 ObjectiveDemo[1146:728047] 1
19:28:14.853 ObjectiveDemo[1146:728048] 2

NSLock還提供了tryLock和lockBeforeDate兩個(gè)方法

  • tryLock 嘗試加鎖,如果鎖已經(jīng)被鎖住,不會(huì)阻塞線程,并返回NO
  • lockBeforeDate 方法會(huì)在所指定Date之前嘗試加鎖,如果在指定時(shí)間之前都不能加鎖,則返回NO
NSLock *lock = [NSLock new];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [lock lock];
    NSLog(@"1");
    [NSThread sleepForTimeInterval:2.0];
    [lock unlock];
});

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    if([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]])
    {
        NSLog(@"lock Success");
        [lock unlock]
    }
    else
    {
        NSLog(@"lock Failure");
    }
});

結(jié)果(注意時(shí)間)
19:36:37.409 ObjectiveDemo[1161:732625] 1
19:36:39.413 ObjectiveDemo[1161:732632] lock Success

NSConditionLock(條件鎖)

NSConditionLock *lock = [[NSConditionLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lockWhenCondition:12];
    NSLog(@"1");
    [lock unlock];
});

[NSThread sleepForTimeInterval:1.0];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    NSLog(@"2");
    [lock unlockWithCondition:12];
});

結(jié)果
09:17:10.458 ObjectiveDemo[659:9989] 2
09:17:10.458 ObjectiveDemo[659:9993] 1

解釋:第一個(gè)線程進(jìn)入時(shí)鎖住,設(shè)置條件為12,第二個(gè)線程運(yùn)行時(shí)鎖住,沒有條件也沒有其它的鎖在執(zhí)行,所以就開始執(zhí)行,完了解開12的鎖,第一線程那里就打開了

注意

  1. 使用initWithCondition初始化鎖時(shí)這時(shí)這把鎖是在當(dāng)前條件下是打開的,默認(rèn)初始條件為0
  2. NSConditionLock的實(shí)例同一時(shí)刻最多只有一個(gè)條件可以解鎖

消費(fèi)者和生產(chǎn)者模型

NSConditionLock *lock = [[NSConditionLock alloc] init];
NSMutableArray *produce = [NSMutableArray array];

//消費(fèi)者
void(^consumer)(NSInteger) = ^(NSInteger condition){
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lockWhenCondition:condition];
        NSLog(@"consumer: %@", produce.firstObject);
        [produce removeObjectAtIndex:0];
        [lock unlock];
    });
};

//生成者
void(^producer)(NSInteger num) = ^(NSInteger num){
    for(NSInteger i = 1; i <= num; i++)
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [lock lock];
            [produce addObject:@(i)];
            NSLog(@"producer: %ld", i);
            [NSThread sleepForTimeInterval:1];
            [lock unlockWithCondition:i];
        });
    }
};

consumer(1);
consumer(2);
consumer(3);
producer(3);

結(jié)果
09:30:50.745 ObjectiveDemo[731:18789] producer: 1
09:30:51.750 ObjectiveDemo[731:18780] consumer: 1
09:30:51.751 ObjectiveDemo[731:18813] producer: 2
09:30:52.754 ObjectiveDemo[731:18812] consumer: 2
09:30:52.755 ObjectiveDemo[731:18886] producer: 3
09:30:53.760 ObjectiveDemo[731:18814] consumer: 3

NSCondition 斷言

在使用條件鎖實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者關(guān)系時(shí)由于需要不斷修改關(guān)系所以代碼比較難看,NSCondition可以優(yōu)雅的實(shí)現(xiàn)

NSCondition *condition = [[NSCondition alloc] init];
NSMutableArray *produce = [NSMutableArray array];

//消費(fèi)者
void(^consumer)(void) = ^{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        while(produce.count == 0)
        {
            [condition wait];
        }
        NSLog(@"consumer: %@", produce.firstObject);
        [produce removeObjectAtIndex:0];
        [condition unlock];
    });
};

//生成者
void(^producer)(NSInteger num) = ^(NSInteger num){
    for(NSInteger i = 1; i <= num; i++)
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [condition lock];
            [produce addObject:@(i)];
            NSLog(@"producer: %ld", i);
            [condition signal];
            [NSThread sleepForTimeInterval:1];
            [condition unlock];
        });
    }
};

consumer();
consumer();
consumer();
producer(3);

結(jié)果
09:45:56.659 ObjectiveDemo[802:28917] producer: 1
09:45:57.660 ObjectiveDemo[802:28825] producer: 2
09:45:58.664 ObjectiveDemo[802:28918] producer: 3
09:45:59.669 ObjectiveDemo[802:28852] consumer: 1
09:45:59.669 ObjectiveDemo[802:28853] consumer: 2
09:45:59.670 ObjectiveDemo[802:28817] consumer: 3

注意:signal一次只能喚醒一個(gè)wait,如果有多個(gè)wait可以使用broadcast

1.4 NSRecursiveLock 遞歸鎖

在有需求實(shí)現(xiàn)遞歸調(diào)用,但是實(shí)現(xiàn)部分又得加鎖,如果使用普通鎖,就會(huì)造成死鎖,比如下面

- (void)recursiveCount:(NSInteger)count
{ 
    static NSLock *lock;
    lock = lock ? lock : [NSLock new];
    [lock lock];
    if(count)
    {
        NSLog(@"start:%ld", count);
        [NSThread sleepForTimeInterval:1];
        [self recursiveCount:count - 1];
        NSLog(@"end:%ld", count);
    }
    [lock unlock];
}

解釋:end是無法輸出的,因?yàn)樵谒坝诌f歸調(diào)用了,到了鎖的時(shí)候發(fā)現(xiàn)上一個(gè)鎖還沒完成然后就開始等待,使用遞歸鎖可以解決

- (void)recursiveCount:(NSInteger)count
{
    static NSRecursiveLock *lock;
    lock = lock ? lock : [NSRecursiveLock new];
    [lock lock];
    if(count)
    {
        NSLog(@"start:%ld", count);
        [NSThread sleepForTimeInterval:1];
        [self recursiveCount:count - 1];
        NSLog(@"end:%ld", count);
    }
    [lock unlock];
}

結(jié)果
10:25:29.171 ObjectiveDemo[884:46332] start:2
10:25:30.173 ObjectiveDemo[884:46332] start:1
10:25:31.174 ObjectiveDemo[884:46332] end:1
10:25:31.175 ObjectiveDemo[884:46332] end:2

@synchronized

這是一種非常簡潔而且安全的加鎖方式,內(nèi)部自動(dòng)判斷了當(dāng)前使用加鎖的方式(遞歸鎖的判斷)

- (void)recursiveCount:(NSInteger)count
{
    @synchronized (self) {
        if(count)
        {
            NSLog(@"start:%ld", count);
            [NSThread sleepForTimeInterval:1];
            [self recursiveCount:count - 1];
            NSLog(@"end:%ld", count);
        }
    }
}

注意:@synchronized (obj)只是對(duì)同一個(gè)obj進(jìn)行互斥

dispatch_semaphore_t

這是通過GCD的信號(hào)來加鎖的,在我的GCD中已經(jīng)講過,可以去那了解一下,不怎么建議使用這種方式加鎖。因?yàn)檫@是實(shí)現(xiàn)并發(fā)用的,只是把并發(fā)數(shù)設(shè)置為1,就有了鎖的效果了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 多線程系列篇章計(jì)劃內(nèi)容:iOS多線程編程(一) 多線程基礎(chǔ)[https://juejin.im/post/6890...
    賣饃工程師閱讀 607評(píng)論 0 3
  • 一、前言 前段時(shí)間看了幾個(gè)開源項(xiàng)目,發(fā)現(xiàn)他們保持線程同步的方式各不相同,有@synchronized、NSLock...
    景銘巴巴閱讀 28,793評(píng)論 17 231
  • 1、進(jìn)程 1)進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某次數(shù)據(jù)集合的一次運(yùn)行活動(dòng),它是操作系統(tǒng)分配資源的基本單...
    Crics閱讀 480評(píng)論 0 0
  • 轉(zhuǎn)載自:http://www.lxweimin.com/p/938d68ed832c# 一、前言 前段時(shí)間看了幾個(gè)...
    cafei閱讀 4,553評(píng)論 1 12
  • 一、前言 前段時(shí)間看了幾個(gè)開源項(xiàng)目,發(fā)現(xiàn)他們保持線程同步的方式各不相同,有@synchronized、NSLock...
    稻春閱讀 470評(píng)論 0 0