iOS設計模式之(二)生產者-消費者

本文首發於 個人我的博客html

前言

維基百科中,這麼描述 生產者消費者問題git

生產者消費者問題(英語:Producer-consumer problem),也稱有限緩衝問題(英語:Bounded-buffer problem),是一個多進程同步問題的經典案例。該問題描述了共享固定大小緩衝區的兩個進程——即所謂的「生產者」和「消費者」——在實際運行時會發生的問題。生產者的主要做用是生成必定量的數據放到緩衝區中,而後重複此過程。與此同時,消費者也在緩衝區消耗這些數據。該問題的關鍵就是要保證生產者不會在緩衝區滿時加入數據,消費者也不會在緩衝區中空時消耗數據。github

要解決該問題,就必須讓生產者在緩衝區滿時休眠(要麼乾脆就放棄數據),等到下次消費者消耗緩衝區中的數據的時候,生產者才能被喚醒,開始往緩衝區添加數據。一樣,也可讓消費者在緩衝區空時進入休眠,等到生產者往緩衝區添加數據以後,再喚醒消費者。一般採用進程間通訊的方法解決該問題,經常使用的方法有信號燈法[1]等。若是解決方法不夠完善,則容易出現死鎖的狀況。出現死鎖時,兩個線程都會陷入休眠,等待對方喚醒本身。該問題也能被推廣到多個生產者和消費者的情形。數據庫

場景

咱們公司本身項目中,有個場景,就是IM消息,當咱們收到消息時候,進行一些業務邏輯的處理,還有數據庫的操做,而後刷新列表。存在的問題是,若是消息接收的特別快,例如離線消息,可能登錄的是,有幾百條消息拉取下來,若是每一條每一條的處理,將會致使兩個問題:bash

  • 上次刷新還沒完成,下次就進來了。致使界面閃的問題
  • 每條消息進行一次寫入數據庫操做,IO操做耗時,因此致使,性能問題嚴重

解決方案

上述問題,使用生產者-消費者就能解決這個問題dom

爲了簡單高效。咱們用計時器,間隔0.1秒接收一條消息,刷新列表,假設須要2秒。async

代碼

定義變量

@property (nonatomic,strong) NSMutableArray *array;//存放數據
@property (nonatomic,strong) dispatch_semaphore_t semaphore;
複製代碼

開啓定時器

NSTimer *curTimer =[NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(producerFuncWithNumber:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:curTimer forMode:NSDefaultRunLoopMode];
[curTimer fire];
複製代碼

假設0.1秒收到一條數據

//生產者
- (void)producerFuncWithNumber:(NSInteger )number{
    
    number = random()%10;
    //生產者生成數據
    dispatch_queue_t t = dispatch_queue_create("222222", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(t, ^{
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        
        [self.array addObject:[NSString stringWithFormat:@"%ld",(long)number]];
        NSLog(@"生產了%lu 個",(unsigned long)self.array.count);
        dispatch_semaphore_signal(self.semaphore);
        
    });
}
複製代碼

消費者

//消費者
- (void)consumerFunc{
    
    dispatch_queue_t t1 = dispatch_queue_create("11111", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(t1, ^{
        
        while (YES) {
            if (self.array.count > 0) {
                dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
                NSLog(@"消費了%lu 個",(unsigned long)self.array.count);
                [self.array removeAllObjects];
                [self reload];
                dispatch_semaphore_signal(self.semaphore);
                
            }
        }
    });
}
複製代碼

每次刷新的時候,假設用時2秒

-(void)reload{
    NSLog(@"休眠2秒");
    sleep(2);
}
複製代碼

完整代碼以下

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic,strong) NSMutableArray *array;//存放數據
@property (nonatomic,strong) dispatch_semaphore_t semaphore;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    //開啓計時器
    NSTimer *curTimer =[NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(producerFuncWithNumber:) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:curTimer forMode:NSDefaultRunLoopMode];
    [curTimer fire];
    
    [self consumerFunc];
}

-(void)reload{
    NSLog(@"休眠2秒");
    sleep(2);
}
- (NSMutableArray *)array{
    if (!_array) {
        _array = [NSMutableArray array];
    }
    return  _array;
}

- (dispatch_semaphore_t)semaphore{
    if (!_semaphore) {
        _semaphore = dispatch_semaphore_create(1);
    }
    return _semaphore;
}

//生產者
- (void)producerFuncWithNumber:(NSInteger )number{
    
    number = random()%10;
    //生產者生成數據
    dispatch_queue_t t = dispatch_queue_create("222222", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(t, ^{
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        
        [self.array addObject:[NSString stringWithFormat:@"%ld",(long)number]];
        NSLog(@"生產了%lu 個",(unsigned long)self.array.count);
        dispatch_semaphore_signal(self.semaphore);
        
    });
}

//消費者
- (void)consumerFunc{
    
    dispatch_queue_t t1 = dispatch_queue_create("11111", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(t1, ^{
        
        while (YES) {
            if (self.array.count > 0) {
                dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
                NSLog(@"消費了%lu 個",(unsigned long)self.array.count);
                [self.array removeAllObjects];
                [self reload];
                dispatch_semaphore_signal(self.semaphore);
                
            }
        }
    });
}
@end
複製代碼

輸出結果

iOS-生產者消費者[5508:75404] 生產了1 個
iOS-生產者消費者[5508:75407] 生產了2 個
iOS-生產者消費者[5508:75406] 生產了3 個
iOS-生產者消費者[5508:75411] 生產了4 個
iOS-生產者消費者[5508:75440] 生產了5 個
iOS-生產者消費者[5508:75443] 生產了6 個
iOS-生產者消費者[5508:75450] 生產了7 個
iOS-生產者消費者[5508:75458] 生產了8 個
iOS-生產者消費者[5508:75463] 生產了9 個
iOS-生產者消費者[5508:75472] 生產了10 個
iOS-生產者消費者[5508:75480] 生產了11 個
iOS-生產者消費者[5508:75481] 生產了12 個
iOS-生產者消費者[5508:75482] 生產了13 個
iOS-生產者消費者[5508:75494] 生產了14 個
iOS-生產者消費者[5508:75518] 生產了15 個
iOS-生產者消費者[5508:75521] 生產了16 個
iOS-生產者消費者[5508:75526] 生產了17 個
iOS-生產者消費者[5508:75528] 生產了18 個
iOS-生產者消費者[5508:75531] 生產了19 個
iOS-生產者消費者[5508:75545] 生產了20 個
iOS-生產者消費者[5508:75405] 消費了20 個
iOS-生產者消費者[5508:75405] 休眠2秒
iOS-生產者消費者[5508:75545] 生產了1 個
iOS-生產者消費者[5508:75531] 生產了2 個
iOS-生產者消費者[5508:75528] 生產了3 個
iOS-生產者消費者[5508:75526] 生產了4 個
iOS-生產者消費者[5508:75521] 生產了5 個
iOS-生產者消費者[5508:75518] 生產了6 個
iOS-生產者消費者[5508:75494] 生產了7 個
iOS-生產者消費者[5508:75482] 生產了8 個
iOS-生產者消費者[5508:75481] 生產了9 個
iOS-生產者消費者[5508:75480] 生產了10 個
iOS-生產者消費者[5508:75472] 生產了11 個
iOS-生產者消費者[5508:75463] 生產了12 個
iOS-生產者消費者[5508:75458] 生產了13 個
iOS-生產者消費者[5508:75450] 生產了14 個
iOS-生產者消費者[5508:75443] 生產了15 個
iOS-生產者消費者[5508:75440] 生產了16 個
iOS-生產者消費者[5508:75406] 生產了17 個
iOS-生產者消費者[5508:75407] 生產了18 個
iOS-生產者消費者[5508:75404] 生產了19 個
iOS-生產者消費者[5508:75411] 生產了20 個
iOS-生產者消費者[5508:75405] 消費了20 個
iOS-生產者消費者[5508:75405] 休眠2秒
iOS-生產者消費者[5508:75411] 生產了1 個
iOS-生產者消費者[5508:75404] 生產了2 個

。。。
複製代碼

由輸出結果可知,每次完成業務邏輯須要2秒的話,能夠等待上次完成,再進行下次取數據,此時,已經有了20條數據,能夠一次性處理,對性能是個挺大的提高。oop

注意點

生產者和消費者各自在信號量處理,爲了保證數據的惟一性,須要用信號量 dispatch_semaphore_t semaphore 來保證多條線程不擁擠,不搶數據。性能

總結

以上就是對生產者消費者的簡單實用,實際使用的時候,能夠靈活實用,有時候能有挺大的優化空間。優化

Demo地址

相關文章
相關標籤/搜索