https://www.cnblogs.com/fengmin/p/5841014.html參考源碼解讀
[圖片上傳中...(截屏2019-12-15下午11.26.52.png-95caa7-1576423615938-0)]
一.線程間通信以及各自的特點(diǎn)
通信
// 1.創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 2.添加操作
[queue addOperationWithBlock:^{
// 異步進(jìn)行耗時操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
// 回到主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 進(jìn)行一些 UI 刷新等操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
}];
}];
GCD
performSelectorInbackground:
performSelectorOnMainThread
二.比較:
NSOperation底層是GCD更加面向?qū)ο蟾炱瘢强梢垣@得隊列執(zhí)行狀態(tài)才菠,設(shè)置線程依賴贰盗,線程優(yōu)先級,可以取消線程,可以控制最大并發(fā)量丹弱。
GCD更加簡單高效。
二.NSOperation
1,控制最大并發(fā)數(shù)
// 創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 設(shè)置最大并發(fā)操作數(shù)
// queue.maxConcurrentOperationCount = 2;
queue.maxConcurrentOperationCount = 1; // 就變成了串行隊列
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
[queue addOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
2,最吸引人的地方是它能添加操作之間的依賴關(guān)系
比如說有A、B兩個操作歇万,其中A執(zhí)行完操作,B才能執(zhí)行操作梨与,那么就需要讓B依賴于A
//操作依賴
-
(void)addDependency
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
}];[op2 addDependency:op1]; // 讓op2 依賴于 op1堕花,則先執(zhí)行op1,在執(zhí)行op2
[queue addOperation:op1];
[queue addOperation:op2];
}
//可以看到粥鞋,無論運(yùn)行幾次缘挽,其結(jié)果都是op1先執(zhí)行,op2后執(zhí)行呻粹。
三.隊列的取消壕曼、暫停、和恢復(fù)
取消隊列的所有操作
-(void)cancelAllOperations;
提示:也可以調(diào)用NSOperation的-(void)cancel方法取消單個操作等浊。
暫停和恢復(fù)隊列
-(void)setSuspended:(BOOL)b; // YES表示暫停隊列 NO表示恢復(fù)隊列
-(BOOL)isSuspend;
獲取隊列操作數(shù):
operationCount(只讀屬性)
注意:
(1)暫停不會刪除隊列內(nèi)的操作腮郊。只是把隊列掛起。暫停和掛起都是針對隊列而言的筹燕。暫停后還可以重新恢復(fù)接著原來的任務(wù)進(jìn)行執(zhí)行轧飞。
(2)取消全部任務(wù)的操作會清空隊列里的所有任務(wù)衅鹿。
(3)暫停和取消都是對隊列里的操作而言的,而正在執(zhí)行的操作是無法取消或暫停的过咬。
三 .GCD
dispatch_sync和dispatch_async用來控制是否要開啟新的線程
隊列的類型泵三,決定了任務(wù)執(zhí)行的方式(串行,并發(fā))(dispatch_get_main_queue()書串行隊列)
還有一種情況栓霜,先并發(fā)同時執(zhí)行任務(wù)1翠桦,任務(wù)二,等他倆執(zhí)行完胳蛮,再通知任務(wù)三任務(wù)四并發(fā)執(zhí)行销凑。就是下面的截圖代碼。兩個notify被通知到仅炊,同時在queue這個并發(fā)隊列里去執(zhí)行任務(wù)即可斗幼!
dispatch_group_enter :通知 group,下個任務(wù)要放入 group 中執(zhí)行了
dispatch_group_leave: 通知 group,任務(wù)成功完成,要移除,與 enter成對出現(xiàn)
dispatch_group_wait: 在任務(wù)組完成時調(diào)用,或者任務(wù)組超時是調(diào)用(完成指的是enter和leave次數(shù)一樣多)
dispatch_group_notify: 只要任務(wù)全部完成了,就會在最后調(diào)用
- (void)test {
NSURL *url = [NSURL URLWithString:@"http://upload-images.jianshu.io/upload_images/1432482-dcc38746f56a89ab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"];
SDWebImageManager *manager = [SDWebImageManager sharedManager];
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[manager loadImageWithURL:url options:SDWebImageRefreshCached progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"下載完成了");
});
}
//信號量 也可以用信號量的方式去做
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
//創(chuàng)建全局并行
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務(wù)一
dispatch_group_async(group, queue, ^{
[self getAdvertList:^(BOOL iscomple) {
dispatch_semaphore_signal(semaphore);
}];
});
//任務(wù)二
dispatch_group_async(group, queue, ^{
[self getHotCultureList:^(BOOL iscomple) {
dispatch_semaphore_signal(semaphore);
}];
});
dispatch_group_notify(group, queue, ^{
//6個任務(wù),6個信號等待.
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//這里就是所有異步任務(wù)請求結(jié)束后執(zhí)行的代碼
//[self.homeTableView.mj_header endRefreshing];
//這里兩個網(wǎng)絡(luò)請求結(jié)束后抚垄。獲取到的蜕窿。一個參數(shù)用于第三個借口參數(shù)
NSLog(@"------------4444444444444444-xin hao-------%@", self.imeiStr);
多線程安全
不是所有多線程都加鎖,如果只是讀取數(shù)據(jù)呆馁,那么不會對數(shù)據(jù)造成安全隱患桐经,當(dāng)多個線程對數(shù)據(jù)同時讀寫的時候才需要加鎖保證線程安全
鎖:
(1)OSSpinLock 自旋鎖(high level高級鎖 不休眠)
(2) os_unfair_lock(它是互斥鎖)
(3)pthread_mutex
生產(chǎn)者-消費(fèi)者模式:同時進(jìn)行的貌矿,但是需要先生產(chǎn)出產(chǎn)品才可以賣給消費(fèi)者
總結(jié):
- (instancetype)init
{
if (self = [super init]) {
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化鎖
pthread_mutex_init(&_mutex, &attr);
// 銷毀屬性
pthread_mutexattr_destroy(&attr);
// 初始化條件
pthread_cond_init(&_cond, NULL);
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest
{ //子線程同時進(jìn)行 所以不確定先調(diào)用哪個方法
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 生產(chǎn)者-消費(fèi)者模式
// 線程1
// 刪除數(shù)組中的元素
- (void)__remove
{
pthread_mutex_lock(&_mutex);
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待 ??????等待期間會放開這把鎖炭菌,這個是可能就會調(diào)用添加方法給它加鎖,當(dāng)添加完調(diào)用signal逛漫,就會喚醒加這個條件的鎖的線程黑低,注意??其實(shí)這個時候是繼續(xù)等待狀態(tài)等待別人放開這把鎖,也是等添加方法執(zhí)行unlock解開這個鎖的時候酌毡,pthread_cond_wait它才可以再次加鎖克握,就繼續(xù)往下走去remove,執(zhí)行完了解開這個鎖枷踏。
pthread_cond_wait(&_cond, &_mutex);
}
[self.data removeLastObject];
NSLog(@"刪除了元素");
pthread_mutex_unlock(&_mutex);
}
// 線程2
// 往數(shù)組中添加元素
- (void)__add
{
pthread_mutex_lock(&_mutex);//初始化鎖
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信號 激活這個等待條件的線程
pthread_cond_signal(&_cond);
// 廣播
// pthread_cond_broadcast(&_cond);
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{ //銷毀資源
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
(4)NSLock,NSRecursiveLock菩暗。NSRecursiveLock遞歸鎖表示可處理同一方法內(nèi)部多次上鎖,表示它可以允許同一線程對其多次加鎖旭蠕,而不會引起死鎖的問題停团,主要用在循環(huán)和遞歸調(diào)用中。也是基于mutex實(shí)現(xiàn)的下梢,所以效率肯定低于mutex
(5)NSCondition
@interface NSConditionDemo()
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation NSConditionDemo
- (instancetype)init
{
if (self = [super init]) {
self.condition = [[NSCondition alloc] init];
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 生產(chǎn)者-消費(fèi)者模式
// 線程1
// 刪除數(shù)組中的元素
- (void)__remove
{
[self.condition lock];
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"刪除了元素");
[self.condition unlock];
}
// 線程2
// 往數(shù)組中添加元素
- (void)__add
{
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信號
[self.condition signal];
// 廣播
// [self.condition broadcast];
[self.condition unlock];
}
注意
(6)NSConditionLock
(6)dispatch_queue
(6) dispatch_semaphore
注意:wait和signal的順序。當(dāng)創(chuàng)建時value為0時那么需要先signal+1似袁,然后再wait洞辣。注意使用順序和信號量的關(guān)系
? dispatch_semaphore_create(value)
創(chuàng)建信號量,value一般情況下傳0
? dispatch_semaphore_wait()
等待信號量昙衅,會對信號量減1(value - 1)扬霜,當(dāng)信號量 < 0 時,會阻塞當(dāng)前線程而涉,等待信號(signal)著瓶,當(dāng)信號量 >= 0時,會執(zhí)行wait后面的代碼
? dispatch_semaphore_signal()
信號量加1啼县,當(dāng)信號量 >= 0 會執(zhí)行wait之后的代碼材原。
因此dispatch_semaphore_wait()和dispatch_semaphore_signal()要成對使用。
(7)synchronized