多執行緒以及底層實現
阿新 • • 發佈:2019-02-13
一.概念
- 什麼是程序
- 程序是指在系統中正在執行的一個應用程式
- 每個程序之間的是獨立的,每個程序均執行在其專用且受保護的記憶體空間內
- 一個程序至少要有一個執行緒
- 什麼是執行緒
- 一個執行緒要執行任務,必須得有執行緒
- 一個程序(程式)的所有任務都線上程中執行的
- 一個執行緒執行任務是序列的,也就是說一個執行緒,同一時間內,只能執行一個任務
- 多執行緒原理
- 同一時間,CPU只能處理1條執行緒,只有一條執行緒在工作(執行)
- 多執行緒併發(同時)執行,其實質是CPU快速的在多執行緒之間排程(切換)
- 如果執行緒過多,會怎樣?
- CPU在N多條執行緒中排程,會消耗大量的cpu資源
- 每條執行緒被排程執行的頻率越低(執行緒的執行效率低)
- 多執行緒的優點
- 能適當提高程式的執行效率
- 能適當提高資源的利用率(CPU 記憶體利用率等)
- 多執行緒的缺點
- 建立執行緒是有開銷的,iOS下主要成本包括:核心資料結構(大約1KB)、棧空間(子執行緒512KB、主執行緒1MB,也可以使用-setStackSize:設定,但必須是4K的倍數,而且最小是16K),建立執行緒大約需要90毫秒的建立時間
- 如果開啟大量的執行緒,會降低程式的效能
- 程式越多CPU的執行緒上的開銷就越大
- 程式設計更加複雜:執行緒之間的通訊,多執行緒的資料共享
- 什麼是主執行緒
- 一個ios程式執行後,會開啟一條執行緒,稱為主執行緒,或者是UI執行緒
- 主執行緒的主要作用
- 顯示和重新整理UI介面
- 處理UI事件(比如點選事件,滾動事件,拖拽事件等)
- 主執行緒的使用注意
- 別將比較耗時的操作放在主執行緒中,會導致UI介面的卡頓
- 將耗時操作放在子執行緒(後臺執行緒,非主執行緒)
二.多執行緒的4種方案
1.C語言的POSIX介面:#include
+ (NSThread *)mainThread; // 獲得主執行緒
- (BOOL)isMainThread; // 是否為主執行緒
+ (BOOL)isMainThread; // 是否為主執行緒```
- 其他用法
```objc//建立NSThread物件
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:nil object:nil];
//獲取當前執行緒
NSThread *thread1 = [NSThread currentThread];
//設定執行緒的名字
thread.name = @"這是設定執行緒名字的";
[thread setName:@"這也是設定執行緒的名字的"];```
- 執行緒狀態
![這裡寫圖片描述](https://img-blog.csdn.net/20160615133614638)
- 互斥鎖
- @synchronized(鎖物件) { // 需要鎖定的程式碼 }
注意:鎖定1份程式碼只用1把鎖(同個物件),用多把鎖是無效的
- 優點:能有效防止因多執行緒搶奪資源造成的額資料安全問題
- 缺點:需要消耗大量的CPU資源
- 使用前提:多執行緒同一時間搶奪同一塊資源
- 互斥鎖就是使用了執行緒同步技術.就是多執行緒在按順序執行
- OC在定義屬性時有nonatomic和atomic兩種選擇
atomic:原子屬性,為setter方法加鎖(預設就是atomic)
nonatomic:非原子屬性,不會為setter方法加鎖
###1.常用的方法
```objc
//在主執行緒中執行任務
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
//YES表示等Selector執行完後才執行後面
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//開闢一條子執行緒執行任務
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array
//開闢一條子執行緒執行任務
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait
//在後臺執行任務(相當於開闢了一個子執行緒)
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg
//類方法,開闢一條子執行緒執行任務
[NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:@"hello"];
<div class="se-preview-section-delimiter"></div>
//開啟子執行緒,並且要通過手動開啟執行緒,否則無法執行任務的
// 建立執行緒
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:@"Alloc"];
// 開啟執行緒,將執行緒新增到執行緒可排程池裡,等待CPU的排程
[thread start];
<div class="se-preview-section-delimiter"></div>
//執行緒睡眠
//睡眠1秒
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
//執行緒睡眠1秒
[NSThread sleepForTimeInterval:1.0];
<div class="se-preview-section-delimiter"></div>
//執行緒退出,千萬不要在主執行緒退出,否則程式就會出現各種問題,包括奔潰,黑屏,不能響應
[NSThread exit];
<div class="se-preview-section-delimiter"></div>
2.執行緒的取消
// 建立執行緒--> 新建狀態
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];
// 開啟執行緒--> 將執行緒新增到可排程執行緒池中,等待CPU排程
[thread start];
// 睡
[NSThread sleepForTimeInterval:0.2];
// 取消執行緒執行,取消執行緒僅僅是給執行緒打上一個被取消的標記
// 要實現真正取消執行緒的執行需要線上程內部的關鍵節點進行判斷
[thread cancel];
NSLog(@"over");
<div class="se-preview-section-delimiter"></div>
3.執行緒的優先順序
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test:) object:@"---"];
thread.name = @"執行緒A";
//設定執行緒的優先順序,範圍是0到1
thread.threadPriority = 1.0;
[thread start];
<div class="se-preview-section-delimiter"></div>
4.執行緒棧區的大小
// 不管主執行緒還是子執行緒,棧區大小預設都是512kb.
// NSLog(@"stackSize = %zd",[NSThread currentThread].stackSize / 1024);
// 最小的棧區大小是16kb,必須是4的整數倍,建議不要修改棧區大小,使用預設即可.
[NSThread currentThread].stackSize = 1024 * 1024;
NSLog(
@"stackSize = %zd",[NSThread currentThread].stackSize / 1024)
<div class="se-preview-section-delimiter"></div>
5.多執行緒訪問資料導致的資料異常的處理
多個執行緒訪問資料的時候會導致資料異常,這時候我們要使用互斥鎖
// 先保證單條執行緒能正確工作:能夠買完所有票
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.ticket = 20;
// 建立執行緒
NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
threadA.name = @"美女A";
// 開啟執行緒
[threadA start];
// 建立執行緒
NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
threadB.name = @"美女B";
// 開啟執行緒
[threadB start];
}
- (void)saleTicket {
while (YES)
{
// 睡
[NSThread sleepForTimeInterval:1.0];
// 使用互斥鎖:可以保證鎖住的程式碼同一時間只有一個執行緒訪問.
// 使用互斥鎖鎖住的程式碼要儘可能少,鎖住關鍵節點的程式碼即可.
// [[NSUserDefaults standardUserDefaults] synchronize];
//@synchronized
// self:鎖物件,可以是任意NSObject類的物件.
// 注意點:鎖物件必須是所有執行緒物件能同時訪問的物件.
// 如果只有一個地方使用到互斥鎖,一般鎖物件就是self`,避免再建立一個物件.
// NSObject *lockObj = [[NSObject alloc] init];
@synchronized(self)
{
// 判斷是否有剩餘的票數
if(self.ticket > 0)
{
// 如果有,則賣一張
self.ticket --;
NSLog(@"%@賣了一張票,剩餘的票數:%zd",[NSThread currentThread].name, self.ticket);
continue;
}
}
// 沒有票,則提示使用者票沒了
NSLog(@"票沒了");
break;
}
NSLog(@"over");
}
<div class="se-preview-section-delimiter"></div>
拓展:
•nonatomic 與 atomic
1)iOS中還有一種鎖 原子鎖atomic
2)nonatomic 非原子屬性 (執行緒不安全),
3)atomic 原子屬性,預設都是"原子"屬性 (執行緒安全),也不能保證資料寫入的正確性 4)原子屬性,也是一個多執行緒技術,setter/getter函式是一個原子操作,如果多執行緒同時呼叫setter
時,不會出現某一個執行緒執行完setter所有語句之前,另一個執行緒就開始執行setter,相當於函式頭尾 加了鎖. 這樣的話併發訪問效能會比較低.
提示:設定原子屬性後,不要自己去寫原子屬性的setter方法
原因:原子屬性預設的setter方法中,使用了“128位自旋鎖”,效能比互斥鎖高,但是同樣消耗性 能
<div class="se-preview-section-delimiter"></div>
- ##3.C語言的GCD(效能好,程式碼更精簡)
- 什麼是GCD
- 全稱是 Grand Central Dispatch,可譯為“牛逼的中樞排程器”
- 純C語言,提供了非常多強大的函式
- GCD的優勢
- GCD是蘋果公司為多核的並行運算提出的解決方案
- GCD會自動利用更多的CPU核心(比如雙核、四核)
- GCD會自動管理執行緒的生命週期(建立執行緒、排程任務、銷燬執行緒)
- 程式設計師只需要告訴GCD想要執行什麼任務,不需要編寫任何執行緒管理程式碼
- GCD的有四種佇列:序列佇列,並行佇列,全域性佇列,主佇列.
- GCD有兩種操作:非同步操作,同步操作.
- 非同步操作dispatch_async,”會併發執行”,無法確定任務的執行順序
- 同步操作dispatch_sync,”會依次順序執行”,能夠決定任務的執行順序
- 主佇列可以看成序列佇列,如果在主執行緒中使用同步操作,那就會造成執行緒阻塞
- 一個執行緒只能屬於一個程序,而一個程序可以有多個執行緒,但至少有一個執行緒,執行緒是操作
系統可識別的最小執行和排程單位.
1.CGD的常用使用
注意:在主執行緒中,執行同步操作,裡面的任務將不會執行(阻塞),同步非同步決定是否可以開子執行緒,佇列決定任務是怎麼執行的.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// [self gcdDemo1];//cgd的同步和非同步執行任務.
// [self gcdDemo2];
// [self gcdDemo3];
}
//cgd的同步和非同步執行任務.
-(void)gcdDemo1
{
void (^gcdBlock)() = ^()
{
NSLog(@"這是簡單的block,%@",[NSThread currentThread]);
};
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_sync(queue, gcdBlock);//開啟子執行緒
dispatch_async(queue, gcdBlock);//不開啟子執行緒
}
//gcd的常用寫法(精簡版)
- (void)gcdDemo2
{
//在全域性佇列非同步執行
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"async---%@",[NSThread currentThread]);
});
//在主佇列中非同步執行.(沒有開啟子執行緒)
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"async---%@",[NSThread currentThread]);
});
//在主佇列同步執行
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"sync---%@",[NSThread currentThread]);
});
}
//執行緒間的通訊
- (void)gcdDemo3
{
//在非同步開啟子執行緒
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"這是耗時任務---%@",[NSThread currentThread]);
//非同步在主執行緒執行任務
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主執行緒重新整理UI--%@",[NSThread currentThread]);
});
});
}
<div class="se-preview-section-delimiter"></div>
2.CGD中的序列佇列
/// 序列佇列非同步執行
/// 提問:會不會開執行緒? 是否是順序執行?
/// 問答:會開執行緒 開多條 是
- (void)gcdDemo3{
for (int i = 0; i < 10; i++) {
// 引數1:佇列的名稱
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
// 將任務新增到佇列中,並指定執行任務的函式
dispatch_async(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
}
/// 序列佇列同步執行
/// 提問:會不會開執行緒? 是否是順序執行?
/// 問答:不會開執行緒 是
- (void)gcdDemo1 {
// 建立序列佇列
// 引數1:佇列的名稱
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; ++i) {
// 將任務新增到佇列中,並指定執行任務的函式
dispatch_sync(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
}
/// 序列佇列非同步執行
/// 提問:會不會開執行緒? 開幾條執行緒? 是否是順序執行?
/// 問答:會開執行緒 開1條 是 全對
- (void)gcdDemo2 {
// 建立序列佇列
// 引數1:佇列的名稱
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; ++i) {
// 將任務新增到佇列中,並指定執行任務的函式
dispatch_async(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
}
<div class="se-preview-section-delimiter"></div>
3.GCD中的併發佇列
/// 併發佇列同步執行
/// 提問:會不會開執行緒? 開幾條? 是否是順序執行?
/// 答案:不會開 順序
- (void)gcdDemo1 {
// 建立序列佇列
// 引數1:佇列的名稱
for (int i = 0; i < 10; ++i) {
// 將任務新增到佇列中,並指定執行任務的函式
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
}
/// 序列佇列非同步執行
/// 提問:會不會開執行緒? 開幾條執行緒? 是否是順序執行?
/// 問答:會開執行緒 不知道,由底層執行緒池決定 不是
- (void)gcdDemo2 {
// 建立序列佇列
// 引數1:佇列的名稱
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 5; ++i) {
// 將任務新增到佇列中,並指定執行任務的函式
dispatch_async(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
NSLog(@"下載xxxx=%@",[NSThread currentThread]);
NSLog(@"下載B=%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"下載C=%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"下載A=%@",[NSThread currentThread]);
});
}
<div class="se-preview-section-delimiter"></div>
4.GCD中的主佇列(特殊的序列佇列)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"start");
[self gcdDemo3];
NSLog(@"end");
}
///主佇列同步執行不死鎖
- (void)gcdDemo3 {
// 獲得主佇列
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"over = %@",[NSThread currentThread]);
// 同步任務
dispatch_sync(queue, ^{
NSLog(@"%@---%zd",[NSThread currentThread],100);
});
});
}
///主佇列同步執行
- (void)gcdDemo2 {
// 獲得主佇列
dispatch_queue_t queue = dispatch_get_main_queue();
// 同步任務
// 主佇列中的任務必須在主執行緒空閒的時才會執行.
// 主佇列中新增同步任務會造成死鎖.
dispatch_sync(queue, ^{
NSLog(@"%@---%zd",[NSThread currentThread],100);
});
NSLog(@"over");
}
//主佇列非同步執行
- (void)gcdDemo1 {
// 獲得主佇列
dispatch_queue_t queue = dispatch_get_main_queue();
for (int i = 0; i < 5; ++i) {
// 非同步任務
dispatch_async(queue, ^{
NSLog(@"%@---%zd",[NSThread currentThread],i);
});
}
}
<div class="se-preview-section-delimiter"></div>
5.GCD中的全域性佇列
//全域性佇列非同步執行
//全域性佇列特點跟併發佇列是一樣的
// dispatch_get_global_queue:獲得全域性佇列不需要關心什麼時候銷燬
// 自己建立的併發佇列需要在不使用的時候銷燬
// 一般開發第三方框架時會是用自定義併發佇列.
- (void)gcdDemo1 {
// 獲得主佇列
// Flags that are reserved for future use. Always specify 0 for this parameter
// 引數1:
// 引數2:保留給未來使用,永遠給個0
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; ++i) {
// 非同步任務
dispatch_async(queue, ^{
NSLog(@"%@---%zd",[NSThread currentThread],i);
});
}
// 在ARC中不允許呼叫release方法
// dispatch_release(queue);
}
<div class="se-preview-section-delimiter"></div>
6.CGD中的dispatch_barrier_async,barrier非同步的使用
使用 dispatch_barrier_async 新增的 block 會在之前新增的 block 全部執行結束之後,統一在同一個執行緒順序執行,從而保證對非執行緒安全的物件進行正確的操作!
- (void)viewDidLoad {
[super viewDidLoad];
// 建立併發佇列
queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
// dispatch_barrier_async:一定是要使用自定義併發佇列
// queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 20; ++i) {
[self loadImage:i];
}
}
/// 載入index索引指定圖片
// 主要用於在多個非同步操作完成之後,統一對非執行緒安全的物件進行更新
- (void)loadImage:(NSInteger)index{
dispatch_async(queue, ^{
// 獲得圖片名稱
NSString *imageName = [NSString stringWithFormat:@"%zd.jpg",index % 9];
// 獲得圖片路徑
NSString *filePath = [[NSBundle mainBundle] pathForResource:imageName ofType:nil];
// 載入圖片
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
NSLog(@"下載第%zd張圖片,%@",index,[NSThread currentThread]);
dispatch_barrier_async(queue, ^{
NSLog(@"第%ld張圖片下載完成=%@,",(long)index,[NSThread currentThread]);
// 將圖片新增到陣列中
// NSMutableArray不是執行緒安全的類
// NSMutableDictionary也不是執行緒安全的類的
// 凡是帶有mutable單詞的類都不是執行緒安全.
[self.images addObject:image];
});
});
}
<div class="se-preview-section-delimiter"></div>
7.dispatch_after操作和dispatch_once
1.dispatch_after
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC));
//延時操作,這裡是開啟了子執行緒的
dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
NSLog(@"延遲操作是子執行緒:%@",[NSThread currentThread]);
NSLog(@"這是延遲操作!!!");
});
<div class="se-preview-section-delimiter"></div>
2.dispatch_once
//使用dispatch_once實現單例,以及和互斥鎖實現單例的比較
(dispatch_once實現的單例比互斥鎖實現的單例效率高)
// dispatch_once {}
// 1.要提供一個全域性的訪問點
// 2.在這個專案中,只會有一個例項物件
+(instancetype)sharedHttpTool {
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 在這裡寫的程式碼在整個專案執行過程中只會執行一次
NSLog(@"是一次嗎");
instance = [[self alloc] init];
});
return instance;
}
// 使用互斥鎖實現單例
+ (instancetype)sharedSync {
static id instance = nil;
@synchronized(self) {
if (instance == nil) {
instance = [[self alloc] init];
}
}
return instance;
}
<div class="se-preview-section-delimiter"></div>
8.排程組
- (void)gcdDemo1 {
// 組物件
dispatch_group_t group = dispatch_group_create();
// 佇列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"下載圖片1=%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1.5];
NSLog(@"下載圖片2=%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2.5];
NSLog(@"下載圖片3=%@",[NSThread currentThread]);
});
// dispatch_group_notify:當組裡面的所有任務執行完畢,會在主佇列中新增任務.並執行任務
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"更新UI %@",[NSThread currentThread]);
});
NSLog(@"come here");
// dispatch_group_async(group, dispatch_get_main_queue(), ^{
// NSLog(@"更新UI %@",[NSThread currentThread]);;
// });
}
<div class="se-preview-section-delimiter"></div>
9.dispatch_apple的使用
NSArray *arr = @[@1,@2,@3,@4,@5];
CFTimeInterval begin1 = CFAbsoluteTimeGetCurrent();
dispatch_apply(arr.count, dispatch_get_global_queue(0, 0), ^(size_t index)
{
NSLog(@"%@,%@",arr[index],[NSThread currentThread]);
});
CFTimeInterval begin2 = CFAbsoluteTimeGetCurrent();
NSLog(@"%lf",begin1 - begin2);
<div class="se-preview-section-delimiter"></div>
- 4.Objective-C的NSOperation和NSOperationQueue(基於GCD)
###示例程式碼:
1.用start新增任務和開啟執行緒
//新增任務和執行緒的開啟
- (void)opDemo1
{
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
//用start開啟開啟執行緒是在當前執行緒中執行任務的
[op start];
}
- (void)test
{
NSLog(@"it is test method-->%@",[NSThread currentThread]);
}
<div class="se-preview-section-delimiter"></div>
2.operation新增操作和開啟執行緒
- (void)opDemo2
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//設定佇列的併發數
//如果設定為1,那麼就變成了序列佇列
queue.maxConcurrentOperationCount = 2;
for (int i = 0; i < 10; i ++) {
//新增操作
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test:) object:nil];
//往佇列中新增操作就-->開啟了非同步(除了在mainQueue中不開啟子執行緒)
[queue addOperation:op];
}
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"這是block操作-->%@",[NSThread currentThread] );
}];
[[[NSOperationQueue alloc] init] addOperation:op];
}
<div class="se-preview-section-delimiter"></div>
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主執行緒
NSLog(@"下載1------%@", [NSThread currentThread]);
}];
// 新增額外的任務(在子執行緒執行)
[op addExecutionBlock:^{
NSLog(@"下載2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下載3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下載4------%@", [NSThread currentThread]);
}];
[op start];
3.執行緒間通訊
//執行緒間的通訊
- (void)opDemo3
{
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"這是耗時操作--%@",[NSThread currentThread]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"UI更新--%@",[NSThread currentThread]);
}];
}];
}
4.佇列的掛起,開啟以及取消佇列操作
//控制佇列的掛起和開啟的按鈕點選事件
- (IBAction)pauseAndResume {
if(self.queue.operationCount == 0) {
NSLog(@"佇列中沒有操作");
return;
}
// 設定佇列的掛起狀態
// setter = !getter
self.queue.suspended = !self.queue.isSuspended;
// 掛起佇列不會影響正在執行的操作
// operationCount中包含沒有執行完畢的所有操作.
// 佇列如果是掛起的,再往佇列中新增操作,也不會執行
if (self.queue.suspended) {
NSLog(@"暫停 = %zd",self.queue.operationCount);
} else {
NSLog(@"繼續 = %zd",self.queue.operationCount);
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (int i = 0; i < 20; ++i)
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%@--%d", [NSThread currentThread],i);
}];
[self.queue addOperation:op];
}
}
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [[NSOperationQueue alloc] init];
// 設定最大併發數
_queue.maxConcurrentOperationCount = 2;
}
return _queue;
}
5.任務之間新增依賴
- (void)op1
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *bop = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"111bop,%@",[NSThread currentThread]);
}];
//往bop任務中新增額外的任務
[bop addExecutionBlock:^{
NSLog(@"2222---%@",[NSThread currentThread]);
}];
NSInvocationOperation *iop = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(top) object:nil];
NSInvocationOperation *iop2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(top2) object:nil];
//必須先新增依賴,再把任務新增到佇列中
[bop addDependency:iop];
[iop addDependency:iop2];
[queue addOperation:bop];
//新增任務到佇列中
[queue addOperation:iop];
[queue addOperation:iop2];
}