IOS多執行緒使用GCD與訊號量實現生產者與消費者模式
阿新 • • 發佈:2019-02-17
一、原理的簡述
在生產者消費者模式當中,首先需要分清在這個模式當中有哪些角色?
各角色分別擔任什麼職責與它們之間的關係如何?
角色之間是在保證資料的準確性的情況下如何通訊(同步資料)的?
假設現在有一個這樣的情形:
有兩個人共同訪問一個容量有限的倉庫,這2個人,其中一個是生產鞋子的,另一個是售賣鞋子,
他們共同使用一個倉庫。在使用這個倉庫之前,這2人之間需要建立一種規約,即:
1.生產鞋子的人首先需要向倉庫管理員申請鑰匙,在拿到鑰匙的時候需要判斷兩種情況
在滿倉的時候不能再讓生產鞋子的人繼續往這個倉庫中存放鞋子,因為這個倉庫的容量有限,
繼續放會出現爆倉,那麼這個時候生產鞋子的人應該怎麼做呢?很明顯,讓它把倉庫鑰匙
然後出去等待。當倉庫出現有位置空的時候,才繼續存放鞋子。
2.賣鞋子的時候在使用這個倉庫之前也需要向倉庫管理員申請鑰匙,當倉庫有鞋子的時候,
把鞋子拿出來去賣,否則歸還鑰匙出倉等待。
3.這個倉庫只有一把鑰匙,即同時只能限制一個人進來。
二、將問題程式化:
.h檔案:
// // BaseRoom.h // MultiThread // // Created by liuxiaobing on 2018/10/31. // Copyright © 2018 liuxiaobing. All rights reserved. // #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN /** 倉庫 */ @interface BaseRoom : NSObject @property(strong,nonatomic) NSMutableArray* list; @property(assign,nonatomic) BOOL bIsStop; @property(strong,nonatomic) NSMutableArray* baseList; //倉庫 @property(strong,nonatomic) dispatch_semaphore_t mutex; //訪問倉庫(臨界區)的互斥訪問訊號量 @property(strong,nonatomic) dispatch_semaphore_t comsumer_sem; //生產者-是否生產物件的標記 消費者是否消費倉庫物件的標記 使用信 @property(strong,nonatomic) dispatch_semaphore_t product_sem; //生產者-是否生產物件的標記 消費者是否消費倉庫物件的標記 使用信 @property(nonatomic,assign) int count; -(void) produce:(NSString*) e; -(NSString*) comsumer; @end NS_ASSUME_NONNULL_END
.m檔案 :
// // BaseRoom.m // MultiThread // // Created by liuxiaobing on 2018/10/31. // Copyright © 2018 liuxiaobing. All rights reserved. // #import "BaseRoom.h" @implementation BaseRoom - (instancetype)init { self = [super init]; if (self) { //新建一個倉庫,這裡暫不作容量設計 self.baseList = [[NSMutableArray alloc] init]; //初始化生產物件--消費者標記,初始為0表示什麼都沒有 self.product_sem = dispatch_semaphore_create(10); self.comsumer_sem = dispatch_semaphore_create(0); //初始化臨界區互斥訪問訊號量,用訊號量實現互斥,特殊初始值為1. //控制同一時刻只有一個執行緒物件在訪問倉庫 self.mutex = dispatch_semaphore_create(1); } return self; } -(void)produce:(NSString *)e{ long baseCount = dispatch_semaphore_wait(self.mutex, 5 * NSEC_PER_SEC); //先獲取訪問倉庫的訊號量 if(baseCount != 0){ NSLog(@"39----------倉庫有人正在使用,生產者處於等待"); } long maxSpaceCount = dispatch_semaphore_wait(self.product_sem, 5 * NSEC_PER_SEC); //再判斷 倉庫是否還有可放物品的空間 if(maxSpaceCount != 0){ NSLog(@"43----------倉庫10個空間已經使用完,生產者處於等待:倉庫容量:%lu",[self.baseList count]); }else{ [self.baseList addObject:e]; NSLog(@"40---------生產鞋子%@:w倉庫目前有:%lu",e,[self.baseList count]); dispatch_semaphore_signal(self.mutex); //生產完了釋放臨界區的訪問鎖 dispatch_semaphore_signal(self.comsumer_sem); //將倉庫中的皮鞋數量加1 } } -(NSString*) comsumer{ NSString* e = nil; long baseCount = dispatch_semaphore_wait(self.mutex, 5 * NSEC_PER_SEC); //先獲取訪問倉庫的訊號量 if(baseCount != 0){ NSLog(@"55----------倉庫有人正在使用,消費者處於等待"); } long avableCount = dispatch_semaphore_wait(self.comsumer_sem, 5 * NSEC_PER_SEC); //再判斷 倉庫是否還有可取,如果有物品,則取一個出來,否則t等待 if(avableCount != 0){ NSLog(@"59----------空倉,消費者處於等待"); }else{ e = [self.baseList objectAtIndex:[self.baseList count] -1]; [self.baseList removeLastObject]; NSLog(@"50---------賣鞋子:%@ 倉庫還有%lu:",e,[self.baseList count]); dispatch_semaphore_signal(self.mutex); //生產完了釋放臨界區的訪問鎖 dispatch_semaphore_signal(self.product_sem); //將倉庫中的可放置的數量 +1 } return e; } @end
測試頁面:
.h檔案
//
// ProComsumerModel.h
// MultiThread
//
// Created by liuxiaobing on 2018/10/31.
// Copyright © 2018 liuxiaobing. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "BaseRoom.h"
NS_ASSUME_NONNULL_BEGIN
/**
生產者消費者模式
*/
@interface ProComsumerModel : UIViewController
@property(nonatomic,strong) BaseRoom* baseroom;
//生產者執行緒跑的佇列,這個佇列可以控制生產者的執行是並行還是序列
@property(strong,nonatomic)dispatch_queue_t producerQueue;
//消費者執行緒跑的佇列,這個佇列可以控制消費者的執行是並行還是序列
@property(strong,nonatomic) dispatch_queue_t consumerQueue;
-(void) initObj;
@end
NS_ASSUME_NONNULL_END
.m
//
// ProComsumerModel.m
// MultiThread
//
// Created by liuxiaobing on 2018/10/31.
// Copyright © 2018 liuxiaobing. All rights reserved.
//
#import "ProComsumerModel.h"
#import "ProComsumerModel.h"
@interface ProComsumerModel ()
@end
@implementation ProComsumerModel
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self initObj];
[self initView];
}
-(void) initView{
UIButton* button2 = [UIButton buttonWithType:UIButtonTypeCustom];
[button2 setTitle:@"生產" forState:UIControlStateNormal];
button2.frame = CGRectMake(20, 80, 200, 40);
button2.backgroundColor = [UIColor brownColor];
[button2 addTarget:self action:@selector(producer:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button2];
UIButton* button3 = [UIButton buttonWithType:UIButtonTypeCustom];
[button3 setTitle:@"消費" forState:UIControlStateNormal];
button3.frame = CGRectMake(20, 180, 200, 40);
button3.backgroundColor = [UIColor brownColor];
[button3 addTarget:self action:@selector(comsumer:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button3];
}
-(void)producer:(UIButton*) button{
[self producter];
}
-(void)comsumer:(UIButton*) button{
[self comsumer];
}
-(void) initObj{
self.baseroom = [[BaseRoom alloc] init];
//分別建立N個生產者和M消費者各自的執行併發佇列
//均使用併發佇列,即生產者之間可以併發執行,消費者之間也可以併發執行
self.producerQueue = dispatch_queue_create("producer", DISPATCH_QUEUE_CONCURRENT);
self.consumerQueue = dispatch_queue_create("consumer", DISPATCH_QUEUE_CONCURRENT);
}
-(void) producter{
// //建立10個生產者持續生產皮鞋
// for (int i = 0; i < 1; i++) {
//
// }
dispatch_async(self.producerQueue, ^{
while(1){
NSString* t = [NSString stringWithFormat:@"nike"];
[self.baseroom produce:t];
sleep(1);
}
});
}
-(void)comsumer{
dispatch_async(self.consumerQueue, ^{
while(1){
[self.baseroom comsumer];
sleep(2);
}
});
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
測試效果: