多線(xiàn)程(歸納,總結(jié))

總結(jié)一下多線(xiàn)程的東西,防止忘記。。。不然每次都得翻出來(lái)重新看

基礎(chǔ)簡(jiǎn)介

  • 進(jìn)程

  • 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序

  • 每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在某專(zhuān)用且受保護(hù)的內(nèi)存空間內(nèi)

  • 線(xiàn)程

  • 1個(gè)進(jìn)程要想執(zhí)行任務(wù),必須得有線(xiàn)程存在(每一個(gè)進(jìn)程至少有一個(gè)線(xiàn)程)

  • 1個(gè)進(jìn)程(程序)的所有任務(wù)都在線(xiàn)程里完成

Snip20160321_1.png

線(xiàn)程的執(zhí)行是串行的,一個(gè)一個(gè)按順序執(zhí)行
所以一般優(yōu)化程序流暢度,就要開(kāi)啟多個(gè)線(xiàn)程,多個(gè)線(xiàn)程之間是并行的,可以分開(kāi)處理不同的操作,一起執(zhí)行

  • 多線(xiàn)程的原理

  • 同一時(shí)間,CPU只能處理1條線(xiàn)程,只有1條線(xiàn)程在工作(執(zhí)行)

  • 多線(xiàn)程并發(fā)(同時(shí))執(zhí)行,其實(shí)是CPU快速地在多線(xiàn)程之間調(diào)度(切換)

  • 線(xiàn)程不能太多

  • 如果線(xiàn)程過(guò)多,CPU調(diào)度不過(guò)來(lái),會(huì)卡死

  • 每條線(xiàn)程都會(huì)執(zhí)行緩慢

  • 多線(xiàn)程的優(yōu)點(diǎn)

  • 能適當(dāng)提高程序的執(zhí)行效率

  • 能適當(dāng)?shù)奶岣哔Y源利用率(CPU,內(nèi)存的利用率,不會(huì)空轉(zhuǎn))

  • 多線(xiàn)程的缺點(diǎn)

  • 創(chuàng)建線(xiàn)程是有開(kāi)銷(xiāo)的,iOS主要成本包括:

    • 內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)
    • ??臻g(子線(xiàn)程512KB,主線(xiàn)程1MB,也可以使用-setStackSize:設(shè)置,但必須是4K的倍數(shù),而且最小是16K,不建議自己設(shè)定,系統(tǒng)既然設(shè)定了就是有用的),創(chuàng)建線(xiàn)程大約需要90毫秒的創(chuàng)建時(shí)間
    • 線(xiàn)程開(kāi)啟太多,會(huì)降低程序的性能,iOS一般最多5條線(xiàn)程
    • 線(xiàn)程越多,CPU在調(diào)度線(xiàn)程上的開(kāi)銷(xiāo)越大
    • 程序設(shè)計(jì)更加復(fù)雜:比如線(xiàn)程之間的通信,多線(xiàn)程的數(shù)據(jù)共享
  • 主線(xiàn)程

  • 一個(gè)程序運(yùn)行后,默認(rèn)會(huì)開(kāi)啟一條線(xiàn)程,成為"主線(xiàn)程"或者"UI線(xiàn)程"

  • 主線(xiàn)程的作用

  • 顯示\刷新UI界面

  • 處理UI時(shí)間(比如點(diǎn)擊事件\滾動(dòng)事件\拖拽事件等)

  • 主線(xiàn)程的使用注意

  • 耗時(shí)操作放在子線(xiàn)程,不要放在主線(xiàn)程

四中多線(xiàn)程的實(shí)現(xiàn)

Snip20160321_2.png

pthead

純C語(yǔ)言的,這玩意兒基本上不用
創(chuàng)建代碼

// 第一個(gè)傳一個(gè)
pthread_create(<#pthread_t *restrict#>, <#const pthread_attr_t *restrict#>, <#void *(*)(void *)#>, <#void *restrict#>)
第一個(gè)參數(shù)需要?jiǎng)?chuàng)建pthread_t對(duì)象地址,第三個(gè)參數(shù)傳一個(gè)C語(yǔ)言函數(shù),第二個(gè)和第四個(gè)參數(shù)傳NULL

// 例子
    pthread_t thread2;
    pthread_create(&thread2, NULL, buttonClick, NULL);

void * buttonClick(void *param)
{
    for (NSInteger i = 0; i<10000; i++) {
        NSLog(@"------buttonClick---%zd--%@", i, [NSThread currentThread]);
    }
    return NULL;
}

這個(gè)C的東西,反正我沒(méi)用過(guò)...

NSThread

這種多線(xiàn)程的方法是比較輕量級(jí),全程自己手動(dòng)管理生命周期,同步,加鎖等問(wèn)題,封裝了C語(yǔ)言的多線(xiàn)程

  • 動(dòng)態(tài)方法
/**
* selector : 線(xiàn)程執(zhí)行的方法,這個(gè)selector最多只能接收一個(gè)參數(shù)
* target : selector是消息發(fā)送的對(duì)象,發(fā)給誰(shuí)的
* argument : 傳給selector的唯一參數(shù)
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;

// 例子,線(xiàn)程的開(kāi)啟
// 初始化線(xiàn)程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 設(shè)置線(xiàn)程的優(yōu)先級(jí)(0.0 ~ 1.0,1.0是最高級(jí))
thread.threadPriority = 1;
// 設(shè)置線(xiàn)程的名字
thread.name = @"jack";
// 開(kāi)啟線(xiàn)程
[thread start];
  • 其他創(chuàng)建多線(xiàn)程的方法
  • 靜態(tài)方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
// 例子
// 調(diào)用完畢后,會(huì)馬上創(chuàng)建并開(kāi)啟新線(xiàn)程  
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];  
  • 隱式創(chuàng)建線(xiàn)程
[self performSelectorInBackground:@selector(run) withObject:nil]

優(yōu)點(diǎn): 簡(jiǎn)單快捷
缺點(diǎn):無(wú)法對(duì)線(xiàn)程進(jìn)行更詳細(xì)的設(shè)置

相關(guān)方法

+ (NSThread *)mainThread; // 獲得主線(xiàn)程
- (BOOL)isMainThread; // 是否為主線(xiàn)程
+ (BOOL)isMainThread; // 是否為主線(xiàn)程

獲得當(dāng)前線(xiàn)程
NSThread *current = [NSThread currentThread];

線(xiàn)程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
控制線(xiàn)程的狀態(tài)
  • 啟動(dòng)線(xiàn)程
// 進(jìn)入就緒狀態(tài)->運(yùn)行狀態(tài)-- 執(zhí)行完畢 -- > 死亡狀態(tài)
- (void)start;
  • 阻塞(暫停)線(xiàn)程
  • (void)sleepUntilDate:(NSDate *)date;
// 例子
//    [NSThread sleepUntilDate:[NSDate distantFuture]]; // [NSDate distantFuture]從現(xiàn)在開(kāi)始到永遠(yuǎn)
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
  • (void)sleepForTimeInterval:(NSTimeInterval)ti;
    [NSThread sleepForTimeInterval:2]; // 讓線(xiàn)程睡眠2秒(阻塞2秒)
  • 強(qiáng)制死亡狀態(tài)
// 強(qiáng)制停止線(xiàn)程
+ (void)exit;

一旦線(xiàn)程死亡,就必須重新開(kāi)啟新的線(xiàn)程,不能重新調(diào)用

線(xiàn)程鎖

在多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)一個(gè)對(duì)象的時(shí)候,同時(shí)對(duì)該對(duì)象進(jìn)行了操作,就會(huì)出現(xiàn)安全隱患,用NSThread的方法需要自己管理線(xiàn)程鎖,這就是互斥鎖

  • 互斥鎖的使用格式
    @synchronized(鎖對(duì)象) { // 需要鎖定的代碼 }
    // 一份代碼只有一把鎖,多把鎖是無(wú)效的,還是亂的
    // 這個(gè)鎖一般是全局變量,表示唯一的(切記)

互斥鎖的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):能有效防止因多線(xiàn)程搶奪資源造成的數(shù)據(jù)安全問(wèn)題
缺點(diǎn):需要消耗大量的CPU資源

加互斥鎖也就是要打到"線(xiàn)程同步"的問(wèn)題
線(xiàn)程同步的意思是:多條線(xiàn)程在同一條線(xiàn)上執(zhí)行(按順序地執(zhí)行任務(wù))
互斥鎖,就是使用了線(xiàn)程同步技術(shù)
意思就是說(shuō)所有線(xiàn)程上同一對(duì)象是一樣的!!!!

線(xiàn)程之間的通信

在一個(gè)進(jìn)程中,多個(gè)線(xiàn)程之間是有一定關(guān)聯(lián),相互通信的

  • 一般在子線(xiàn)程進(jìn)行復(fù)雜操作,在主線(xiàn)程刷新UI,這就是線(xiàn)程通信
Snip20160321_5.png

常用方法

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// 最后這個(gè)(BOOL)wait是指下面代碼是否等這個(gè)執(zhí)行完了之后再進(jìn)行
  • 蘋(píng)果還提供了第二種線(xiàn)程同步的方法NSMachPort
    NSPort有3個(gè)子類(lèi):NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。
    使用的方式為接收線(xiàn)程中注冊(cè)NSMachPort,在另外的線(xiàn)程中使用此port發(fā)送消息,則被注冊(cè)線(xiàn)程會(huì)收到相應(yīng)消息,然后最終在主線(xiàn)程里調(diào)用某個(gè)回調(diào)函數(shù)。
    可以看到,使用NSMachPort的結(jié)果為調(diào)用了其它線(xiàn)程的1個(gè)函數(shù),而這正是performSelector所做的事情,所以,NSMachPort是個(gè)雞肋。線(xiàn)程間通信應(yīng)該都通過(guò)performSelector來(lái)搞定。

GCD

Grand Central Dispatch(中央調(diào)度)
純C語(yǔ)言,對(duì)NSThread進(jìn)行了封裝

切記:

不要在當(dāng)前穿行隊(duì)列里使用sync函數(shù),因?yàn)闀?huì)卡住線(xiàn)程,調(diào)用sync函數(shù),系統(tǒng)會(huì)馬上去執(zhí)行這個(gè)函數(shù),但是當(dāng)前線(xiàn)程也是串行的,他會(huì)默認(rèn)執(zhí)行完當(dāng)前線(xiàn)程再去執(zhí)行別的,這就造成了沖突,會(huì)卡死

GCD優(yōu)勢(shì):

  • 為多核的并發(fā)運(yùn)算提出的解決方法
  • 自動(dòng)利用更多的CPU內(nèi)核(比如雙核,四核)
  • 自動(dòng)管理線(xiàn)程的生命周期(創(chuàng)建線(xiàn)程,調(diào)度任務(wù),銷(xiāo)毀線(xiàn)程等)
  • 只需要告訴GCD執(zhí)行什么,GCD會(huì)自行管理任務(wù)

GCD兩個(gè)核心概念

  • 任務(wù):執(zhí)行什么操作
  • 隊(duì)列:用來(lái)存放任務(wù)

GCD的使用就2個(gè)步驟

  • 定制任務(wù)
  • 確定想做的事情

將任務(wù)添加到隊(duì)列中

  • GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出,放到對(duì)應(yīng)的線(xiàn)程中執(zhí)行
  • 任務(wù)的取出遵循隊(duì)列的FIFO原則:先進(jìn)先出,后進(jìn)后出

GCD的OC對(duì)象的內(nèi)存管理
GCD支持Cocoa內(nèi)存管理機(jī)制,因此可以在提交到queue的block中自由地使用Objective-C對(duì)象。每個(gè)dispatch queue維護(hù)自己的autorelease pool確保釋放autorelease對(duì)象,但是queue不保證這些對(duì)象實(shí)際釋放的時(shí)間。如果應(yīng)用消耗大量?jī)?nèi)存,并且創(chuàng)建大量autorelease對(duì)象,你需要?jiǎng)?chuàng)建自己的autorelease pool,用來(lái)及時(shí)地釋放不再使用的對(duì)象。

同步和異步的區(qū)別

  • 同步:只能在當(dāng)前線(xiàn)程中執(zhí)行任務(wù),不具備開(kāi)啟線(xiàn)程的能力
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
  • 異步:可以在新的線(xiàn)程中執(zhí)行任務(wù),具備開(kāi)啟線(xiàn)程的能力
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
Snip20160321_7.png

例子

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self syncConcurrent];
}

/**
 * 同步函數(shù) + 主隊(duì)列:
 */
- (void)syncMain
{
    NSLog(@"syncMain ----- begin");
    
    // 1.獲得主隊(duì)列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.將任務(wù)加入隊(duì)列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
    NSLog(@"syncMain ----- end");
}

/**
 * 異步函數(shù) + 主隊(duì)列:只在主線(xiàn)程中執(zhí)行任務(wù)
 */
- (void)asyncMain
{
    // 1.獲得主隊(duì)列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.將任務(wù)加入隊(duì)列
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 同步函數(shù) + 串行隊(duì)列:不會(huì)開(kāi)啟新的線(xiàn)程,在當(dāng)前線(xiàn)程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
 */
// 倆參數(shù),第一個(gè)是名字,第二個(gè)是執(zhí)行的優(yōu)先級(jí)
/**
* 全局并發(fā)隊(duì)列的優(yōu)先級(jí)
* #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
* #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(rèn)(中)
* #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
* #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺(tái)
*/
- (void)syncSerial
{
    // 1.創(chuàng)建串行隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
    
    // 2.將任務(wù)加入隊(duì)列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 異步函數(shù) + 串行隊(duì)列:會(huì)開(kāi)啟新的線(xiàn)程,但是任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
 */
- (void)asyncSerial
{
    // 1.創(chuàng)建串行隊(duì)列
    // 兩個(gè)參數(shù),第一個(gè)是名字,第二個(gè)是穿行隊(duì)列
    // DISPATCH_QUEUE_CONCURRENT 并行隊(duì)列
    // DISPATCH_QUEUE_SERIAL            串行隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
//    默認(rèn)是穿行隊(duì)列,所以第二個(gè)參數(shù)傳NULL也行
![Uploading Snip20160321_7_826971.png . . .]
//    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
    
    // 2.將任務(wù)加入隊(duì)列
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 同步函數(shù) + 并發(fā)隊(duì)列:不會(huì)開(kāi)啟新的線(xiàn)程
 * 因?yàn)檫€是同步函數(shù),所以你的并發(fā)執(zhí)行并沒(méi)有什么卵用
 */
- (void)syncConcurrent
{
    // 1.獲得全局的并發(fā)隊(duì)列
    // 在你程序啟動(dòng)的時(shí)候,實(shí)際上GCD已經(jīng)給你創(chuàng)建了幾個(gè)全局的隊(duì)列,你拿來(lái)用就好了,所以其實(shí)可以不用自己創(chuàng)建
    // 倆參數(shù),第一個(gè)為線(xiàn)程的優(yōu)先級(jí),第二個(gè)為預(yù)留參數(shù),傳0就行了
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.將任務(wù)加入隊(duì)列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
    NSLog(@"syncConcurrent--------end");
}

/**
 * 異步函數(shù) + 并發(fā)隊(duì)列:可以同時(shí)開(kāi)啟多條線(xiàn)程
 */
- (void)asyncConcurrent
{
    // 1.創(chuàng)建一個(gè)并發(fā)隊(duì)列
    // label : 相當(dāng)于隊(duì)列的名字
    // 倆參數(shù),第一個(gè)是線(xiàn)程的名字,第二個(gè)是優(yōu)先級(jí)
//    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
    
    // 1.獲得全局的并發(fā)隊(duì)列
    // 在你程序啟動(dòng)的時(shí)候,實(shí)際上GCD已經(jīng)給你創(chuàng)建了幾個(gè)全局的隊(duì)列,你拿來(lái)用就好了,所以其實(shí)可以不用自己創(chuàng)建
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.將任務(wù)加入隊(duì)列
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"1-----%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"2-----%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"3-----%@", [NSThread currentThread]);
        }
    });
    
    NSLog(@"asyncConcurrent--------end");
//    dispatch_release(queue);
}

GCD里面線(xiàn)程之間的通信

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 圖片的網(wǎng)絡(luò)路徑
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成圖片
        UIImage *image = [UIImage imageWithData:data];
        
        // 回到主線(xiàn)程  (通信)
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
}
GCD的其他使用
  • barrier障礙

把幾個(gè)線(xiàn)程分開(kāi),先執(zhí)行障礙之前的代碼,然后執(zhí)行障礙,最后執(zhí)行障礙以后的代碼

    dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)

例子

- (void)barrier
{
//    dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });
    
    // 設(shè)置障礙
    dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    });
}
  • 延遲執(zhí)行

dispatch_time延遲執(zhí)行的代碼在block里面,第一個(gè)參數(shù)為開(kāi)始時(shí)間,第二個(gè)參數(shù)為延遲的秒數(shù),單位為納米,轉(zhuǎn)化單位NSEC_PER_SEC ,第三個(gè)參數(shù)為執(zhí)行的線(xiàn)程

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        <#code to be executed after a specified delay#>
    });

例子

- (void)delay
{
    NSLog(@"touchesBegan-----");
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"run-----");
    });
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
}

- (void)run
{
    NSLog(@"run-----");
}
  • 一次性代碼(單例要用)
// 一次執(zhí)行代碼,這個(gè)代碼在這一整個(gè)進(jìn)程里就執(zhí)行一次,不會(huì)執(zhí)行第二次
- (void)once
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"------run");
    });
}
  • 快速迭代(我感覺(jué)就是遍歷)
// 用GCD
- (void)apply
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSString *from = @"/Users/xiaomage/Desktop/From";
    NSString *to = @"/Users/xiaomage/Desktop/To";
    
    NSFileManager *mgr = [NSFileManager defaultManager];
    NSArray *subpaths = [mgr subpathsAtPath:from];

    // 快速迭代  block里面有個(gè)參數(shù)為size_t index
    dispatch_apply(subpaths.count, queue, ^(size_t index) {
        NSString *subpath = subpaths[index];
        NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
        NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
        // 剪切
        [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
        
        NSLog(@"%@---%@", [NSThread currentThread], subpath);
    });
}

/**
 * 對(duì)比:傳統(tǒng)文件剪切
 */
- (void)moveFile
{
    NSString *from = @"/Users/xiaomage/Desktop/From";
    NSString *to = @"/Users/xiaomage/Desktop/To";

    NSFileManager *mgr = [NSFileManager defaultManager];
    NSArray *subpaths = [mgr subpathsAtPath:from];

    for (NSString *subpath in subpaths) {
        NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
        NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 復(fù)雜耗時(shí)操作放在子線(xiàn)程       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 剪切
            [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
        });
    }
}
  • 設(shè)置依賴(lài)分組關(guān)系
  • 創(chuàng)建隊(duì)列
  • 創(chuàng)建group組對(duì)象dispatch_group_create()
  • 講操作加到組里
  • 在組里完成所有操作后,通過(guò) dispatch_notify(<#object#>, <#queue#>, <#notification_block#>) 方法執(zhí)行下一步操作,有三個(gè)參數(shù),第一個(gè)是哪一組的,第二個(gè)是在哪個(gè)隊(duì)列,第三個(gè)是要執(zhí)行的方法block
- (void)group
{
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 創(chuàng)建一個(gè)隊(duì)列組
    dispatch_group_t group = dispatch_group_create();
    
    // 1.下載圖片1
    dispatch_group_async(group, queue, ^{
        // 圖片的網(wǎng)絡(luò)路徑
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成圖片
        self.image1 = [UIImage imageWithData:data];
    });
    
    // 2.下載圖片2
    dispatch_group_async(group, queue, ^{
        // 圖片的網(wǎng)絡(luò)路徑
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
        
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成圖片
        self.image2 = [UIImage imageWithData:data];
    });
    
    // 3.將圖片1、圖片2合成一張新的圖片
    dispatch_group_notify(group, queue, ^{
        // 開(kāi)啟新的圖形上下文
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        
        // 繪制圖片
        [self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        [self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        
        // 取得上下文中的圖片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 結(jié)束上下文
        UIGraphicsEndImageContext();
        
        // 回到主線(xiàn)程顯示圖片
        dispatch_async(dispatch_get_main_queue(), ^{
            // 4.將新圖片顯示出來(lái) 
            self.imageView.image = image;
        });
    });
}

NSOperation

NSOperation的作用:配合使用NSOperation和NSOperationQueue也能實(shí)現(xiàn)多線(xiàn)程編程

NSOperation和NSOperationQueue實(shí)現(xiàn)多線(xiàn)程的具體步驟

  1. 先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對(duì)象中
  2. 然后將NSOperation對(duì)象添加到NSOperationQueue中
  3. 系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來(lái)
  4. 將取出的NSOperation封裝的操作放到一條新線(xiàn)程中執(zhí)行

NSOperation是個(gè)抽象類(lèi),并不具備封裝操作的能力,必須使用它的子類(lèi)

使用NSOperation子類(lèi)的方式有3種

  • NSInvocationOperation(不常用)
  • NSBlockOperation
  • 自定義子類(lèi)繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法
NSInvocationOperation
- (void)invocationOperation
{
    // 創(chuàng)建NSInvocationOperation對(duì)象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    // 調(diào)用start方法開(kāi)始執(zhí)行操作
    [op start];
}
  • 默認(rèn)情況下,調(diào)用start方法后并不會(huì)開(kāi)一條新線(xiàn)程去執(zhí)行操作,而是在當(dāng)前線(xiàn)程同步進(jìn)行操作
  • 只有講NSOperation放到一個(gè)NSOperationQueue中,才會(huì)異步執(zhí)行操作
NSBlockOperation

在NSBlockOperation操作里,只要封裝的任務(wù)個(gè)數(shù)大于1,就會(huì)異步執(zhí)行

- (void)blockOperation
{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 在主線(xiàn)程
        NSLog(@"下載1------%@", [NSThread currentThread]);
    }];
    
    // 添加額外的任務(wù)(在子線(xiàn)程執(zhí)行)
    [op addExecutionBlock:^{
        NSLog(@"下載2------%@", [NSThread currentThread]);
    }];

    [op addExecutionBlock:^{
        NSLog(@"下載3------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"下載4------%@", [NSThread currentThread]);
    }];
    
    [op start];
}
NSOperationQueue

NSOperation一般是和NSOperationQueue配合使用的
在NSOperation調(diào)用start方法的時(shí)候,如果沒(méi)有加入NSOperationQueue,那么他們是同步在當(dāng)前線(xiàn)程執(zhí)行的,只有加入了NSOperationQueue,系統(tǒng)會(huì)自動(dòng)給你異步執(zhí)行,而且怎么異步,開(kāi)多少線(xiàn)程都不用你管

兩種方式

  • (void)addOperation:(NSOperation *)op;
  • (void)addOperationWithBlock:(void (^)(void))block;

示例如下
```objc
- (void)operationQueue1
{
    // 創(chuàng)建隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 創(chuàng)建操作(任務(wù))
    // 創(chuàng)建NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
    
    // 創(chuàng)建NSBlockOperation
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"download5 --- %@", [NSThread currentThread]);
    }];
    
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download6 --- %@", [NSThread currentThread]);
    }];
    
    // 創(chuàng)建XMGOperation
    XMGOperation *op5 = [[XMGOperation alloc] init];
    
    // 添加任務(wù)到隊(duì)列中
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]
    [queue addOperation:op4]; // [op4 start]
    [queue addOperation:op5]; // [op5 start]
}

- (void)operationQueue2
{
    // 創(chuàng)建隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 創(chuàng)建操作
    //    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    //        NSLog(@"download1 --- %@", [NSThread currentThread]);
    //    }];
    
    // 添加操作到隊(duì)列中
    //    [queue addOperation:op1];
    [queue addOperationWithBlock:^{
        NSLog(@"download1 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
    }];
}
  • 最大并發(fā)數(shù)
    可以設(shè)置最大并發(fā)數(shù),也就是最多開(kāi)啟的線(xiàn)程數(shù)
    把最大并發(fā)數(shù)設(shè)置成1,就變成了串行
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
  • 取消隊(duì)列的所有操作
- (void)cancelAllOperations;```
提示:也可以調(diào)用NSOperation的- (void)cancel方法取消單個(gè)操作

- 暫停和恢復(fù)隊(duì)列

```objc    
- (void)setSuspended:(BOOL)b; // YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列
- (BOOL)isSuspended;
  • 線(xiàn)程之間的依賴(lài)
    這個(gè)設(shè)置起來(lái)比著GCD要簡(jiǎn)單的多,GCD可以通過(guò)barrier,建組的方式,然而,NSOperation卻只需要一行代碼,而且還可以設(shè)置不同線(xiàn)程之間的依賴(lài)等
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download1----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download2----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"download4----%@", [NSThread  currentThread]);
        }
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download5----%@", [NSThread  currentThread]);
    }];
    op5.completionBlock = ^{
        NSLog(@"op5執(zhí)行完畢---%@", [NSThread currentThread]);
    };
    
    // 設(shè)置依賴(lài)
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    [op3 addDependency:op4];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
}

同樣是下載圖片的依賴(lài)關(guān)系

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
// 想讓block里面改外面的局部變量的值,在外部參數(shù)加__block
    __block UIImage *image1 = nil;
    // 下載圖片1
    NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
        
        // 圖片的網(wǎng)絡(luò)路徑
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成圖片
        image1 = [UIImage imageWithData:data];
    }];
    
    __block UIImage *image2 = nil;
    // 下載圖片2
    NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
        
        // 圖片的網(wǎng)絡(luò)路徑
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];

        
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成圖片
        image2 = [UIImage imageWithData:data];
    }];
    
    // 合成圖片
    NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
        // 開(kāi)啟新的圖形上下文
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        
        // 繪制圖片
        [image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        image1 = nil;
        
        [image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        image2 = nil;
        
        // 取得上下文中的圖片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 結(jié)束上下文
        UIGraphicsEndImageContext();
        
        // 回到主線(xiàn)程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    [combine addDependency:download1];
    [combine addDependency:download2];
    
    [queue addOperation:download1];
    [queue addOperation:download2];
    [queue addOperation:combine];
}
  • 線(xiàn)程之間的通訊
- (void)test
{
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        // 圖片的網(wǎng)絡(luò)路徑
       NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
     
        
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成圖片
        UIImage *image = [UIImage imageWithData:data];
        
        // 回到主線(xiàn)程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • NSThread 第一種:通過(guò)NSThread的對(duì)象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 947評(píng)論 0 3
  • Object C中創(chuàng)建線(xiàn)程的方法是什么?如果在主線(xiàn)程中執(zhí)行代碼,方法是什么?如果想延時(shí)執(zhí)行代碼、方法又是什么? 1...
    AlanGe閱讀 1,908評(píng)論 0 17
  • .一.進(jìn)程 進(jìn)程:是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專(zhuān)用且受保護(hù)的內(nèi)存空...
    IIronMan閱讀 4,601評(píng)論 1 33
  • 在這篇文章中,我將為你整理一下 iOS 開(kāi)發(fā)中幾種多線(xiàn)程方案,以及其使用方法和注意事項(xiàng)。當(dāng)然也會(huì)給出幾種多線(xiàn)程的案...
    張戰(zhàn)威ican閱讀 684評(píng)論 0 0
  • “哎,哎,你趕緊下來(lái),你真大哥啊,你還活不活要不要命了,你知道剛才有多危險(xiǎn),你是不是傻啊,真服你了?!?“你什么你...
    白頭少日記閱讀 396評(píng)論 0 5

友情鏈接更多精彩內(nèi)容