iOS中实现多线程的技术方案
pthread 實現多線程操作
代碼實現:
void * run(void *param) {for (NSInteger i = 0; i < 1000; i++) {NSLog(@"---buttonclick---%zd---%@", i, [NSThread currentThread]);}return NULL; }@implementation ViewController- (IBAction)clickButton:(id)sender {// 定義一個線程pthread_t thread;// 創建一個線程 ?(參1)pthread_t *restrict:創建線程的指針,(參2)const pthread_attr_t *restrict:線程屬性 ?(參3)void *(*)(void *):線程執行的函數的指針,(參4)void *restrict:nullpthread_create(&thread, NULL, run, NULL);// 何時回收線程不需要你考慮pthread_t thread2;pthread_create(&thread2, NULL, run, NULL);}NSThread實現多線程
一個 NSThread 對象就代表一條線程
創建線程的多種方式
-
第一種方式:先創建再啟動線程
?// 創建線程NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"jack"];// 線程啟動了,事情做完了才會死, 一個NSThread對象就代表一條線程[thread start]; -
第二種:直接創建并啟動線程
?// 直接創建并啟動線程[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"jack"]; -
第三種:
?// 直接創建并啟動線程[self performSelectorInBackground:@selector(run:) withObject:@"jack"];// 使線程進入阻塞狀態[NSThread sleepForTimeInterval:2.0];#pragma mark - 執行run方法- (void)run:(NSString *)param{// 當前線程是否是主線程for (NSInteger i = 0; i < 100; i++) {NSLog(@"---%@---%zd---%d", [NSThread currentThread], i, ?[NSThread isMainThread]);}} -
方法2和方法3的優點:快捷 方法1的優點:可以輕松拿到線程
線程間通信
-
線程間通信的體現
1個線程傳遞數據給另1個線程
在1個線程中執行完特定任務后,轉到另1個線程繼續執行任務
線程間通信的常用方法:小程序圖片下載
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {// 獲取圖片的urlNSURL *url = [NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]; // 另開1條線程 object用于數據的傳遞NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downLoadWithURL:) object:url];// 由于下面下載圖片的耗時太長,應領開啟線程來完成[thread start]; }// 下載圖片 - (void)downLoadWithURL:(NSURL *)url {NSLog(@"%@", [NSThread currentThread]);// 下載圖片NSData *data = [NSData dataWithContentsOfURL:url];// 生成圖片UIImage *image = [UIImage imageWithData:data];// 返回主線程顯示圖片[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES]; }以上兩種方式使用線程已經過時了,開發中我們操作線程大多都使用 GCD 和 NSOperation 來實現多線程操作。
下面我就給大家系統的介紹一下 GCD 是如何實現多線程的
GCD 實現多線程
GCD 簡介
GCD 全稱是Grand Central Dispatch,可譯為“超級厲害的中樞調度器”,GCD 是蘋果公司為多核的并行運算提出的解決方案, GCD會自動利用更多的 CPU 內核(比如雙核、四核)來開啟線程執行任務,GCD 會自動管理線程的生命周期(創建線程、調度任務、銷毀線程),不需要我們程序員手動管理內存。
任務和隊列
任務:在同步函數和異步函數中執行
隊列:用來存放任務(并發 串行)
GCD會自動將隊列中的任務取出,放到對應的線程,任務的取出遵循FIFO,即先入先出隊列,First Input First Output 的縮寫。先進入的任務先完成并結束,再執行后面的任務。
同步函數和異步函數,并發隊列和串行隊列
-
用同步的方式執行任務:在當前線程中可立即執行任務,不具備開啟線程的能力
-
用異步的方式執行任務:在當前線程結束時執行任務,具備開啟新的線程的能力
-
并發隊列:允許多個任務同時執行
-
串行隊列:一個任務執行完畢后,再執行下一個任務
創建并發/串行隊列代碼:
// 創建并發隊列 // 參1:const char *label 隊列名稱 // 參2:dispatch_queue_attr_t attr 隊列類型 dispatch_queue_t queueConcurrent = dispatch_queue_create("520it.com", DISPATCH_QUEUE_CONCURRENT);// 創建串行隊列 ?serial 串行 ?concurrent并發 dispatch_queue_t queueSerial = dispatch_queue_create("520it.com", DISPATCH_QUEUE_SERIAL);// 獲取全局隊列 全局隊列是并發隊列 // 參1:隊列的優先級 // 參2:0(以后可能用到的參數) dispatch_queue_t queueGlobal = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 全局并發隊列的優先級 #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(中) #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低 #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺// 獲取主隊列 ?在主隊列中的任務都會在主線程中執行。 dispatch_queue_t queueMain = dispatch_get_main_queue();同步/異步函數代碼表示:
// GCD同步函數串行隊列(立即執行,當前線程) // 參1: dispatch_queue_t queue 隊列 // 參2: 任務 dispatch_sync(queueSerial, ^{for (NSInteger i = 0; i < 10; i++) {NSLog(@"~~~%@", [NSThread currentThread]);} });// 同步函數并行隊列(立即執行,當前線程) dispatch_sync(queueConcurrent, ^{for (NSInteger i = 0; i < 10; i++) {NSLog(@"~~~%@", [NSThread currentThread]);} });// 異步函數串行隊列 (另開線程,多個任務按順序執行) dispatch_async(queueSerial, ^{dispatch_async(queueSerial, ^{for (NSInteger i = 0; i < 10; i++) {NSLog(@"~~~%@", [NSThread currentThread]);}});dispatch_async(queueSerial, ^{for (NSInteger i = 0; i < 10; i++) {NSLog(@"~~~%@", [NSThread currentThread]);}});dispatch_async(queueSerial, ^{for (NSInteger i = 0; i < 10; i++) {NSLog(@"~~~%@", [NSThread currentThread]);}}); });// 異步函數并行隊列 (另開線程,多個任務一起執行) dispatch_async(queueConcurrent, ^{dispatch_async(queueSerial, ^{for (NSInteger i = 0; i < 10; i++) {NSLog(@"~~~%@", [NSThread currentThread]);}});dispatch_async(queueSerial, ^{for (NSInteger i = 0; i < 10; i++) {NSLog(@"~~~%@", [NSThread currentThread]);}});dispatch_async(queueSerial, ^{for (NSInteger i = 0; i < 10; i++) {NSLog(@"~~~%@", [NSThread currentThread]);}}); });// 主隊列:(任何一個任務只要在主隊列中,都會加入到主線程的隊列中執行)注意:使用sync函數(同步函數)往當前串行隊列中添加任務,會卡住當前的串行隊列
解釋:使用同步函數添加任務 A 到串行隊列,說明要在當前串行隊列立即執行任務 A ,任務 A 執行完后,才會執行任務 A 后面的代碼。但當前隊列是串行隊列,也就是說任務 A 必須等到當前串行隊列中正在執行的任務 B 完成之后才能執行,因此又必須先執行任務 A 中立即執行任務,又要必須等到任務 B 執行完以后才能執行下一個任務,所以就會卡死。你等我,我等你,誰也無法執行。
GCD實現線程通信
小項目:下載圖片
代碼如下:
// 獲取圖片的url NSURL *url = [NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"];// 開啟線程下載圖片 dispatch_queue_t queue = dispatch_queue_create("111", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{NSData *data = [NSData dataWithContentsOfURL:url];UIImage *image = [UIImage imageWithData:data];// 下載完成后返回主線程顯示圖片dispatch_async(dispatch_get_main_queue(), ^{self.imageView.image = image;}); });GCD其他常用函數
dispatch_barrier 柵欄
// 1.barrier : 在barrier前面的先執行,然后再執行barrier,然后再執行barrier后面的 barrier的queue不能是全局的并發隊列 dispatch_queue_t queue = dispatch_queue_create("11", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{for (int i = 0; ?i < 100; i++) {NSLog(@"%@--1", [NSThread currentThread]);} });dispatch_async(queue, ^{for (int i = 0; ?i < 100; i++) {NSLog(@"%@--2", [NSThread currentThread]);} });dispatch_barrier_async(queue, ^{for (int i = 0; ?i < 100; i++) {NSLog(@"%@--3", [NSThread currentThread]);} });dispatch_async(queue, ^{for (int i = 0; ?i < 100; i++) {NSLog(@"%@--4", [NSThread currentThread]);} });dispatch_after 延遲執行
// 延遲執行 // 方法1 [self performSelector:@selector(run:) withObject:@"參數" afterDelay:2.0];// 方法2 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{for (NSInteger i = 0; i < 100; i++) {NSLog(@"%@", [NSThread currentThread]);} });// 方法3 [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];dispatch_once 整個程序運行中執行一次
// 整個程序中只執行一次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{// 一次性代碼 });作用:實現某個類的單粒對象
單例模式:在整個應用程序中,共享一份資源(這份資源只需要創建初始化1次)
static id _person; + (instancetype)sharePerson {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{_person = [[super alloc] init];});return _person; }+ (instancetype)allocWithZone:(struct _NSZone *)zone {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{_person = [super allocWithZone:zone];});return _person; }- (id)copy {return _person; }開發中一般自定義成宏,比較方便,一行代碼搞定。
dispatch_apply 快速迭代
示例小程序:將一個文件夾中的圖片剪切到另一個文件夾
// 將圖片剪切到另一個文件夾里 NSString *from = @"/Users/Ammar/Pictures/壁紙"; NSString *to = @"/Users/Ammar/Pictures/to"; NSFileManager *manager = [NSFileManager defaultManager]; NSArray *subPaths = [manager subpathsAtPath:from];// 快速迭代 dispatch_apply(subPaths.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {NSLog(@"%@ - %zd", [NSThread currentThread], index);NSString *subPath = subPaths[index];NSString *fromPath = [from stringByAppendingPathComponent:subPath];NSString *toPath = [to stringByAppendingPathComponent:subPath];// 剪切[manager moveItemAtPath:fromPath toPath:toPath error:nil];NSLog(@"%@---%zd", [NSThread currentThread], index); });dispatch_group 隊列組
示例小程序:需求下載圖片1 下載圖片2 將圖片1和圖片2合成新的圖片
// 創建隊列
? ?dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
GCD定時器
GCD定時器不受Mode影響因此比NSTimer要準確
static int count = 0; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 這句話的意思現在很好懂了 });// GCD定時器 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 1.創建一個定時器源// 參1:類型定時器 // 參2:句柄 // 參3:mask傳0 // 參4:隊列 ?(注意:dispatch_source_t本質是OC對象,表示源) self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);// 嚴謹起見,時間間隔需要用單位int64_t,做乘法以后單位就變了 // 下面這句代碼表示回調函數時間間隔是多少 int64_t interval = (int64_t)(2.0 * NSEC_PER_SEC); // 如何設置開始時間 CGD給我們了一個設置時間的方法 ? // 參1:dispatch_time_t when 傳一個時間, delta是增量dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)); // 從現在起3秒后開始// 2.設置定時器的各種屬性// 參1:timer // 參2:開始時間 // 參3:時間間隔 // 參4:傳0 不需要 ? DISPATCH_TIME_NOW 表示現在 GCD 時間用 NS 表示 dispatch_source_set_timer(self.timer, start, interval, 0);// 3.設置回調(即每次間隔要做什么事情) dispatch_source_set_event_handler(self.timer, ^{NSLog(@"----------------%@", [NSThread currentThread]);// 如果希望做5次就停掉count++;if (count == 5) {dispatch_cancel(self.timer);self.timer = nil;} });// 4.啟動定時器 ?(恢復) dispatch_resume(self.timer);講完 GCD 就該講講 NSOperation,它是 GCD 的面向對象的封裝,使用起來也更方便,
NSOperation實現多線程
NSOperation是個抽象類,并不具備封裝操作的能力,必須使用它的子類
NSInvocationOperation NSBlockOperation 自定義子類繼承NSOperation,實現內部相應的方法使用 NSOperation 實現多線程的步驟:
創建任務 NSOperation 對象 創建 NSOperationQueue 隊列 將任務 NSOperation 對象 add 到 NSOperationQueue 隊列中去NSInvocationOperation
代碼如下:
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];[op start];注意:默認情況下,調用了start方法后并不會開一條新的線程去執行,而是在當前線程同步執行操作,只有將 NSOperation 放到一個 NSOperationQueue 中,才會異步執行操作
NSBlockOperation
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{// 在主線程NSLog(@"下載1------%@", [NSThread currentThread]); }];// 添加額外的任務(在子線程執行),封裝數大于1才會異步執行 [op addExecutionBlock:^{NSLog(@"下載2------%@", [NSThread currentThread]); }];自定義Operation:需要實現- (void)main方法,需要做的事情放在mian方法中
NSOperationQueue
使用NSOperationQueue創建隊列:主隊列和全局隊列
// 創建一個其他隊列(包括串行隊列和并發隊列) 放到這個隊列中的NSOperation對象會自動放到子線程中執行NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 創建一個主隊列,放到這個隊列中的NSOperation對象會自動放到子線程中執行 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];// 表示并發數量:即同時執行任務的最大數。 queue.maxConcurrentOperationCount = 1;隊列的取消、暫停、恢復:
// NSOpertion的 - cancel 方法也可以停止單個操作 - (void)cancelAllOperations; // YES代表暫停隊列,NO代表恢復隊列 - (void)setSuspended:(BOOL)b;添加依賴
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"download1 -------------- %@", [NSThread currentThread]); }];NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"download2 -------------- %@", [NSThread currentThread]); }];NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"download3 -------------- %@", [NSThread currentThread]); }];// 添加依賴: block1 和 block2執行完后 再執行 block3 ?block3依賴于block1和block2// 給block3添加依賴 讓block3在block1和block2之后執行 [block3 addDependency:block1]; [block3 addDependency:block2];[queue addOperation:block1]; [queue addOperation:block2]; [queue addOperation:block3];注意:不能循環依賴,但可以跨隊列依賴,不管NSOperation對象在哪個隊列。只要是兩個NSOperation對象就可以依賴
線程間通信
示例:下載圖片
// 下載圖片 operation實現線程間通信 [[[NSOperationQueue alloc] init] addOperation:[NSBlockOperation blockOperationWithBlock:^{UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]]];// 返回主線程[[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{self.imageView.image = image;}]];}]];示例:下載圖片1和圖片2 并合成圖片
NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 下載圖片1 __block UIImage *image1 = nil; NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]]]; }];// 下載圖片2 __block UIImage *image2 = nil; NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]]];}];CGFloat imageW = self.imageView.bounds.size.width; CGFloat imageH = self.imageView.bounds.size.height;// 合成圖片 NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{UIGraphicsBeginImageContext(CGSizeMake(imageW, imageH));[image1 drawInRect:CGRectMake(0, 0, imageW * 0.5, imageH)];[image2 drawInRect:CGRectMake(0.5 * imageW, 0, 0.5 * imageW, imageH)];UIImage *image3 = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();// 切換回主線程顯示圖片[[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{self.imageView.image = image3;}]];}];// 設置依賴 [block3 addDependency:block1]; [block3 addDependency:block2];// 添加任務到隊列中 [queue addOperation:block1]; [queue addOperation:block2]; [queue addOperation:block3];應用
應用:SDWebImage 框架的底層主要功能實現就是基于多線程,使用多線程,我們可以實現小圖片的多圖片下載。這里的邏輯其實是比較復雜的
實現小圖片的多圖片下載思路:
代碼實現見本文代碼。
本文代碼見:Multithreading
https://github.com/lizhaoLoveIT/Multithreading轉載于:https://www.cnblogs.com/Ice-snowPride/p/5167048.html
總結
以上是生活随笔為你收集整理的iOS中实现多线程的技术方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS多线程技术
- 下一篇: redhat 7部署squid(代理)服