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

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

基礎(chǔ)簡介

  • 進(jìn)程

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

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

  • 線程

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

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

Snip20160321_1.png

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

  • 多線程的原理

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

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

  • 線程不能太多

  • 如果線程過多,CPU調(diào)度不過來,會卡死

  • 每條線程都會執(zhí)行緩慢

  • 多線程的優(yōu)點

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

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

  • 多線程的缺點

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

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

  • 一個程序運行后,默認(rèn)會開啟一條線程,成為"主線程"或者"UI線程"

  • 主線程的作用

  • 顯示\刷新UI界面

  • 處理UI時間(比如點擊事件\滾動事件\拖拽事件等)

  • 主線程的使用注意

  • 耗時操作放在子線程,不要放在主線程

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

Snip20160321_2.png

pthead

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

// 第一個傳一個
pthread_create(<#pthread_t *restrict#>, <#const pthread_attr_t *restrict#>, <#void *(*)(void *)#>, <#void *restrict#>)
第一個參數(shù)需要創(chuàng)建pthread_t對象地址,第三個參數(shù)傳一個C語言函數(shù),第二個和第四個參數(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;
}

這個C的東西,反正我沒用過...

NSThread

這種多線程的方法是比較輕量級,全程自己手動管理生命周期,同步,加鎖等問題,封裝了C語言的多線程

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

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

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

相關(guān)方法

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

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

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

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

線程鎖

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

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

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

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

線程之間的通信

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

  • 一般在子線程進(jìn)行復(fù)雜操作,在主線程刷新UI,這就是線程通信
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;
// 最后這個(BOOL)wait是指下面代碼是否等這個執(zhí)行完了之后再進(jìn)行
  • 蘋果還提供了第二種線程同步的方法NSMachPort
    NSPort有3個子類:NSSocketPort诗良、NSMessagePort、NSMachPort督禽,但在iOS下只有NSMachPort可用。
    使用的方式為接收線程中注冊NSMachPort,在另外的線程中使用此port發(fā)送消息菠红,則被注冊線程會收到相應(yīng)消息第岖,然后最終在主線程里調(diào)用某個回調(diào)函數(shù)。
    可以看到,使用NSMachPort的結(jié)果為調(diào)用了其它線程的1個函數(shù)遇绞,而這正是performSelector所做的事情摹闽,所以舵匾,NSMachPort是個雞肋谎替。線程間通信應(yīng)該都通過performSelector來搞定。

GCD

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

切記:

不要在當(dāng)前穿行隊列里使用sync函數(shù)亡蓉,因為會卡住線程,調(diào)用sync函數(shù)硫麻,系統(tǒng)會馬上去執(zhí)行這個函數(shù)爸邢,但是當(dāng)前線程也是串行的,他會默認(rèn)執(zhí)行完當(dāng)前線程再去執(zhí)行別的拿愧,這就造成了沖突杠河,會卡死

GCD優(yōu)勢:

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

GCD兩個核心概念

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

GCD的使用就2個步驟

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

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

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

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

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

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

例子

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

/**
 * 同步函數(shù) + 主隊列:
 */
- (void)syncMain
{
    NSLog(@"syncMain ----- begin");
    
    // 1.獲得主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.將任務(wù)加入隊列
    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ù) + 主隊列:只在主線程中執(zhí)行任務(wù)
 */
- (void)asyncMain
{
    // 1.獲得主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.將任務(wù)加入隊列
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 同步函數(shù) + 串行隊列:不會開啟新的線程测蹲,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的鬼吵,執(zhí)行完一個任務(wù)励幼,再執(zhí)行下一個任務(wù)
 */
// 倆參數(shù)判耕,第一個是名字鞭呕,第二個是執(zhí)行的優(yōu)先級
/**
* 全局并發(fā)隊列的優(yōu)先級
* #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 // 后臺
*/
- (void)syncSerial
{
    // 1.創(chuàng)建串行隊列
    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
    
    // 2.將任務(wù)加入隊列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

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

/**
 * 同步函數(shù) + 并發(fā)隊列:不會開啟新的線程
 * 因為還是同步函數(shù),所以你的并發(fā)執(zhí)行并沒有什么卵用
 */
- (void)syncConcurrent
{
    // 1.獲得全局的并發(fā)隊列
    // 在你程序啟動的時候坠敷,實際上GCD已經(jīng)給你創(chuàng)建了幾個全局的隊列妙同,你拿來用就好了射富,所以其實可以不用自己創(chuàng)建
    // 倆參數(shù),第一個為線程的優(yōu)先級粥帚,第二個為預(yù)留參數(shù)胰耗,傳0就行了
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.將任務(wù)加入隊列
    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ā)隊列:可以同時開啟多條線程
 */
- (void)asyncConcurrent
{
    // 1.創(chuàng)建一個并發(fā)隊列
    // label : 相當(dāng)于隊列的名字
    // 倆參數(shù),第一個是線程的名字芒涡,第二個是優(yōu)先級
//    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
    
    // 1.獲得全局的并發(fā)隊列
    // 在你程序啟動的時候柴灯,實際上GCD已經(jīng)給你創(chuàng)建了幾個全局的隊列,你拿來用就好了费尽,所以其實可以不用自己創(chuàng)建
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.將任務(wù)加入隊列
    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里面線程之間的通信

- (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];
        
        // 回到主線程  (通信)
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
}
GCD的其他使用
  • barrier障礙

把幾個線程分開赠群,先執(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里面查描,第一個參數(shù)為開始時間,第二個參數(shù)為延遲的秒數(shù)柏卤,單位為納米冬三,轉(zhuǎn)化單位NSEC_PER_SEC ,第三個參數(shù)為執(zhí)行的線程

    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í)行代碼缘缚,這個代碼在這一整個進(jìn)程里就執(zhí)行一次长豁,不會執(zhí)行第二次
- (void)once
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"------run");
    });
}
  • 快速迭代(我感覺就是遍歷)
// 用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里面有個參數(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);
    });
}

/**
 * 對比:傳統(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ù)雜耗時操作放在子線程       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 剪切
            [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
        });
    }
}
  • 設(shè)置依賴分組關(guān)系
  • 創(chuàng)建隊列
  • 創(chuàng)建group組對象dispatch_group_create()
  • 講操作加到組里
  • 在組里完成所有操作后,通過 dispatch_notify(<#object#>, <#queue#>, <#notification_block#>) 方法執(zhí)行下一步操作忙灼,有三個參數(shù)匠襟,第一個是哪一組的,第二個是在哪個隊列该园,第三個是要執(zhí)行的方法block
- (void)group
{
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 創(chuàng)建一個隊列組
    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, ^{
        // 開啟新的圖形上下文
        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();
        
        // 回到主線程顯示圖片
        dispatch_async(dispatch_get_main_queue(), ^{
            // 4.將新圖片顯示出來 
            self.imageView.image = image;
        });
    });
}

NSOperation

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

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

  1. 先將需要執(zhí)行的操作封裝到一個NSOperation對象中
  2. 然后將NSOperation對象添加到NSOperationQueue中
  3. 系統(tǒng)會自動將NSOperationQueue中的NSOperation取出來
  4. 將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行

NSOperation是個抽象類,并不具備封裝操作的能力里初,必須使用它的子類

使用NSOperation子類的方式有3種

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

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

- (void)blockOperation
{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 在主線程
        NSLog(@"下載1------%@", [NSThread currentThread]);
    }];
    
    // 添加額外的任務(wù)(在子線程執(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方法的時候泣特,如果沒有加入NSOperationQueue,那么他們是同步在當(dāng)前線程執(zhí)行的挑随,只有加入了NSOperationQueue状您,系統(tǒng)會自動給你異步執(zhí)行,而且怎么異步,開多少線程都不用你管

兩種方式

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

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

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

```objc    
- (void)setSuspended:(BOOL)b; // YES代表暫停隊列,NO代表恢復(fù)隊列
- (BOOL)isSuspended;
  • 線程之間的依賴
    這個設(shè)置起來比著GCD要簡單的多柒桑,GCD可以通過barrier弊决,建組的方式,然而魁淳,NSOperation卻只需要一行代碼丢氢,而且還可以設(shè)置不同線程之間的依賴等
- (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è)置依賴
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    [op3 addDependency:op4];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
}

同樣是下載圖片的依賴關(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:^{
        // 開啟新的圖形上下文
        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();
        
        // 回到主線程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    [combine addDependency:download1];
    [combine addDependency:download2];
    
    [queue addOperation:download1];
    [queue addOperation:download2];
    [queue addOperation:combine];
}
  • 線程之間的通訊
- (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];
        
        // 回到主線程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末先改,一起剝皮案震驚了整個濱河市疚察,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仇奶,老刑警劉巖貌嫡,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異该溯,居然都是意外死亡岛抄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門狈茉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夫椭,“玉大人,你說我怎么就攤上這事氯庆〔淝铮” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵堤撵,是天一觀的道長仁讨。 經(jīng)常有香客問我,道長实昨,這世上最難降的妖魔是什么洞豁? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮荒给,結(jié)果婚禮上丈挟,老公的妹妹穿的比我還像新娘。我一直安慰自己志电,他們只是感情好曙咽,可當(dāng)我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著溪北,像睡著了一般桐绒。 火紅的嫁衣襯著肌膚如雪夺脾。 梳的紋絲不亂的頭發(fā)上之拨,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天茉继,我揣著相機(jī)與錄音,去河邊找鬼蚀乔。 笑死烁竭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吉挣。 我是一名探鬼主播派撕,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼睬魂!你這毒婦竟也來了终吼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤氯哮,失蹤者是張志新(化名)和其女友劉穎际跪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喉钢,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡姆打,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肠虽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幔戏。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖税课,靈堂內(nèi)的尸體忽然破棺而出闲延,到底是詐尸還是另有隱情,我是刑警寧澤韩玩,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布慨代,位于F島的核電站,受9級特大地震影響啸如,放射性物質(zhì)發(fā)生泄漏侍匙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一叮雳、第九天 我趴在偏房一處隱蔽的房頂上張望想暗。 院中可真熱鬧,春花似錦帘不、人聲如沸说莫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽储狭。三九已至互婿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辽狈,已是汗流浹背慈参。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留刮萌,地道東北人驮配。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像着茸,于是被迫代替她去往敵國和親壮锻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容

  • NSThread 第一種:通過NSThread的對象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 801評論 0 3
  • Object C中創(chuàng)建線程的方法是什么涮阔?如果在主線程中執(zhí)行代碼猜绣,方法是什么?如果想延時執(zhí)行代碼敬特、方法又是什么掰邢? 1...
    AlanGe閱讀 1,740評論 0 17
  • .一.進(jìn)程 進(jìn)程:是指在系統(tǒng)中正在運行的一個應(yīng)用程序,每個進(jìn)程之間是獨立的,每個進(jìn)程均運行在其專用且受保護(hù)的內(nèi)存空...
    IIronMan閱讀 4,486評論 1 33
  • 在這篇文章中擅羞,我將為你整理一下 iOS 開發(fā)中幾種多線程方案尸变,以及其使用方法和注意事項。當(dāng)然也會給出幾種多線程的案...
    張戰(zhàn)威ican閱讀 603評論 0 0
  • “哎减俏,哎召烂,你趕緊下來,你真大哥啊娃承,你還活不活要不要命了奏夫,你知道剛才有多危險,你是不是傻啊历筝,真服你了酗昼。” “你什么你...
    白頭少日記閱讀 311評論 0 5