多線程

概念

進程:一個正在運行的程序叫一個進程刑峡,進程擁有獨立運行所需的全部資源。

線程:線程是指進程內(nèi)的一塊執(zhí)行單元,也是執(zhí)行任務(wù)的基本單位。一個程序,或者說,一個進程,都會有一個或多個線程,如果只有一個,我們叫它主線程,主線程負責(zé)用戶能看見的任務(wù),例如:添加控件,刷新頁面,除了主線程以外,都叫子線程,線程之間是獨立的,并沒有任何聯(lián)系,子線程一般負責(zé)用戶之間看不到的任務(wù),例如,加載圖片的過程,下載視頻等

線程要明確的:只要用戶看的見的,或者跟用戶看的見的有關(guān)的,咱們都是用主線程操作,因為開啟子線程操作的時候,是為了更好的用戶體驗,用戶體驗直接表現(xiàn)為:看到的或者點擊的流暢

線程是耗費資源的,雖然多線程操作,可以提高用戶體驗,但是,不建議,進行很多線程同時進行操作
一個進程是由一或多個線程組成。進程只負責(zé)資源的調(diào)度和分配,線程才是程序真正的執(zhí)行單元签则,負責(zé)代碼的執(zhí)行。

與進程的區(qū)別:

(1)地址空間:進程內(nèi)的一個執(zhí)行單元铐料;進程至少有一個線程渐裂;它們共享進程的地址空間;而進程有自己獨立的地址空間。

(2)資源擁有:進程是資源分配和擁有的單位钠惩,同一個進程內(nèi)的線程共享進程的資源柒凉。

(3)線程是處理器調(diào)度的基本單位,但進程不是篓跛。

(4)二者均可并發(fā)執(zhí)行膝捞。

線程的主要內(nèi)容:

單線程:每個正在運行的程序(即進程),至少包含一個線程愧沟,這個線程叫主線程蔬咬。

1.主線程在程序啟動時被創(chuàng)建,用于執(zhí)行 main 函數(shù)沐寺。

2.只有一個主線程的程序林艘,稱作單線程程序。

3.主線程負責(zé)執(zhí)行程序的所有代碼(UI 展現(xiàn)以及刷新混坞,網(wǎng)絡(luò)請求狐援,本地存儲等等)。這些代碼只能順序執(zhí)行究孕,無法并發(fā)執(zhí)行啥酱。

多線程

1.擁有多個線程的程序,稱作多線程程序

2.iOS 允許用戶自己開辟新的線程蚊俺,相對于主線程來講懈涛,這些線程,稱作子線程泳猬。

3.可以根據(jù)需要開辟若干子線程

4.子線程和主線程都是獨立的運行單元,各自的執(zhí)行互不影響宇植,因此能夠并發(fā)執(zhí)行得封。

3、單指郁、多線程區(qū)別

單線程程序:只有一個線程忙上,代碼順序執(zhí)行,容易出現(xiàn)代碼阻塞(頁面假死)闲坎。

多線程程序:有多個線程疫粥,線程間獨立運行茬斧,能有效的避免代碼阻塞,并且提高程序的運行性能梗逮。

注意:iOS 中關(guān)于 UI 的添加和刷新必須在主線程中操作项秉。

iOS 平臺下的多線程

iOS 多線程實現(xiàn)種類

  • NSThread
  • NSOperationQueue
  • pthread
  • GCD

NSThread

NSThread 是一個輕量級的多線程,它有以下兩種創(chuàng)建方法:

  • 初始化一個子線程慷彤,但需要手動開啟
    - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
  • 初始化一個子線程并自動開啟
    + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument
  • 開啟子線程
    start
  • 取消當(dāng)前子線程
    cancel
  • 開啟后臺執(zhí)行任務(wù)的方法:
    [self performSelectorInBackground:@selector(run:) withObject:@"nsobject thread"];

注意:

1娄蔼、在多線程方法中需要添加自動釋放池

2、在應(yīng)用程序打開的時候底哗,系統(tǒng)會自動為主線程創(chuàng)建一個自動釋放池

3岁诉、我們手動創(chuàng)建的子程序需要我們手動添加自動釋放池

  
   // NSThread 輕量級的線程,可以開啟線程和終止線程
   // 創(chuàng)建一個線程,其實就是給他一個方法去執(zhí)行
   // 創(chuàng)建一個子線程,專門用來打印
  
   NSThread *thread = [[NSThread alloc] initWithTarget: self selector:@selector(actionButtonNslog) object:nil];
  
   // 開啟這個線程
   [thread start];
  
   // 線程操作的時候
   // 在主線程的時候,系統(tǒng)自動給咱們添加了一個自動釋放池,那么咱們再開啟子線程的時候,也要添加一個自動釋放池
   // 如果你的線程開的比較多,會造成代碼比較亂,閱讀性不高
   // 一般方法中,添加了自動釋放池,基本上都是線程方法

NSOperationQueue

NSOperation 也是抽象類 沒有實現(xiàn)具體功能
NSInvocationOperation 調(diào)用操作(相當(dāng)于任務(wù))
NSBlockOperation 在block 操作
NSOperationQueue 線程隊列

  // 創(chuàng)建任務(wù)一
    NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocation1) object:nil];
    // 創(chuàng)建任務(wù)二
    NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
        // block塊中就相當(dāng)于添加的任務(wù)
        [self blockOP2];
    }];
   
    // 創(chuàng)建一個隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 注意 添加任務(wù)前要設(shè)置依賴關(guān)系
    // 依賴性(串行)
    [invocation addDependency:block2];
    // 把任務(wù) 添加進隊列當(dāng)中
    [queue addOperation:invocation];
    [queue addOperation:block2];
    // 設(shè)置線程最大并發(fā)數(shù)
    queue.maxConcurrentOperationCount = 2;
  
    // 需求 進行同步請求一張圖片 不產(chǎn)生屏幕卡頓
    // 思路: 開啟一個子線程 進行同步請求圖片 圖片到手后 把圖片返回到主線程 顯示到ImageView上
}

// 實現(xiàn)任務(wù)一
- (void)invocation1
{
    // 添加一個自動釋放池
    @autoreleasepool {
        // [NSThread currentThread] 當(dāng)前線程的信息
        // [NSThread isMainThread] 是否是主線程
        // 打印出來額number 是線程的個數(shù)
        NSLog(@"%@ 是不是主線程:%d", [NSThread currentThread], [NSThread isMainThread]);
    }
   
}
// 創(chuàng)建任務(wù)二
- (void)blockOP2
{
    // 添加釋放池
    @autoreleasepool {
       
        NSLog(@"%@ 是不是主線程:%d", [NSThread currentThread], [NSThread isMainThread]);
    }
}
- (void)actionButton
{
    if ([self.imageView isAnimating] == NO) {
        [self.imageView startAnimating];
    } else {
        [self.imageView stopAnimating];
    }
}
- (void)anotherActionButton
{
    // 添加一個自動釋放池
    @autoreleasepool {
        for (int i = 0; i < 88888; i++) {
            NSLog(@"%d", i);
        }
    }
   
}

pthread

不經(jīng)常使用
特點:
1 一套通用的多線程API
2 適用于Unix\Linux\Windows等系統(tǒng)
3 跨平臺\可移植
4 使用難度大
使用語言:c語言

GCD

GCD簡介

  1. Grand Central Dispatch 簡稱(CGD),是蘋果公司開發(fā)的技術(shù)跋选。以優(yōu)化應(yīng)用程序支持多核心處理器和其他的對稱多處理系統(tǒng)的系統(tǒng)涕癣。( 對稱多處理"(Symmetrical Multi-Processing)簡稱SMP,是指在一個計算機上匯集了一組處理器(多CPU),各CPU之間共享內(nèi)存子系統(tǒng)以及總線結(jié)構(gòu)前标。它是相對非對稱多處理技術(shù)而言的属划、應(yīng)用十分廣泛的并行技術(shù)。)
  2. GCD 屬于函數(shù)級的多線程候生,性能更高同眯,功能也更加強大。
  3. 它首次發(fā)布在 Mac OS X 10.6 , iOS4及以上也可以使用唯鸭。

GCD 核心概念

  1. 任務(wù):具有一定功能的代碼段须蜗。一般是一個 block 或者函數(shù)。
  2. 分發(fā)隊列:GCD 以隊列的方式進行工作目溉,F(xiàn)IFO明肮。
  3. GCD 會根據(jù)分發(fā)隊列的類型,創(chuàng)建合適數(shù)量的線程執(zhí)行隊列中的任務(wù)缭付。
  • 主隊列 是GCD自帶的一種特殊串行隊列柿估,放在主隊列中的任務(wù),都會放在主線程中執(zhí)行 dispatch_get_main_queue()
  • 全局隊列 隊列 任務(wù)執(zhí)行方式 并發(fā)多個任務(wù)同時執(zhí)行陷猫,串行一個一個執(zhí)行
    1 串行隊列 讓任務(wù)一個接一個地執(zhí)行 (一個任務(wù)執(zhí)行完畢后秫舌,再執(zhí)行下一個任務(wù))
    2 并發(fā)隊列 可以多個任務(wù)同時執(zhí)行 (自動開啟多個線程同時執(zhí)行任務(wù))
    3 主隊列 專門負責(zé)調(diào)度主線程任務(wù),沒有辦法開辟新線程绣檬,任務(wù)在主線程只會順序執(zhí)行
    執(zhí)行方式 執(zhí)行的順序 同步按順序執(zhí)行足陨,異步不按順序執(zhí)行
    1.同步執(zhí)行 在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
    2.異步執(zhí)行 在新的線程中執(zhí)行任務(wù)娇未,具備開啟新線程的能力
    任務(wù) block
    死鎖 如果向主隊列中添加一個同步任務(wù)會死鎖
    死鎖原因:我們知道dispatch_sync表示同步的執(zhí)行任務(wù)墨缘,也就是說執(zhí)行dispatch_sync后,當(dāng)前隊列會阻塞。而dispatch_sync中的block如果要在當(dāng)前隊列中執(zhí)行镊讼,就得等待當(dāng)前隊列程執(zhí)行完成宽涌。
    主隊列在執(zhí)行dispatch_sync,隨后隊列中新增一個任務(wù)block蝶棋。因為主隊列是同步隊列卸亮,所以block要等dispatch_sync執(zhí)行完才能執(zhí)行,但是dispatch_sync是同步派發(fā)嚼松,要等block執(zhí)行完才算是結(jié)束嫡良。在主隊列中的兩個任務(wù)互相等待,導(dǎo)致了死鎖献酗。

GCD 中兩種隊列

dispatch queue分為下面兩種:

  • SerialQueue:一次只執(zhí)行一個任務(wù)寝受。Serial queue通常用于同步訪問特定的資源或數(shù)據(jù)。當(dāng)你創(chuàng)建多個Serial queue時罕偎,雖然它們各自是同步執(zhí)行的很澄,但Serial queue與Serial queue之間是并發(fā)執(zhí)行的。Serial queue能實現(xiàn)線程同步颜及。
  • Concurrent:可以并發(fā)的執(zhí)行多個任務(wù)甩苛,但是遵守 FIFO。

GCD功能

  • dispatch_async( ) 往隊列中添加任務(wù)俏站,任務(wù)會排隊執(zhí)行
  • dispatch_after( ) 往隊列中添加任務(wù)讯蒲,任務(wù)不但會排隊,還會在延遲的時間點執(zhí)行
  • dispatch_apply( ) 往隊列中添加任務(wù)肄扎,任務(wù)會重復(fù)執(zhí)行 n次
  • dispatch_group_async( ) 將任務(wù)添加到隊列中墨林,并添加分組標(biāo)記
  • dispatch_group_notify( ) 將任務(wù)添加到隊列中,當(dāng)某個分組的所有任務(wù)執(zhí)行完之后犯祠,此任務(wù)才會執(zhí)行
  • dispatch_barrier_async( ) 將任務(wù)添加到隊列中旭等,此任務(wù)執(zhí)行的時候,其他任務(wù)停止執(zhí)行
  • dispatch_once( ) 任務(wù)添加到隊列中衡载,但任務(wù)在程序運行過程中搔耕,只執(zhí)行一次
  • dispatch_sync ( ) 將任務(wù)添加到隊列中,block 不執(zhí)行完痰娱,下面代碼不會執(zhí)行
  • dispatch_async_f ( ) 將任務(wù)添加到隊列中弃榨,任務(wù)是函數(shù)非 block

線程間的通信

線程間通信分為兩種:

  • 主線程進入子線程(前面的方法都可以)
  • 子線程回到主線程
    返回主線程:
    GCD:dispatch_get_main_queue()
    NSObject : - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

線程互斥

線程互斥場景:

  • 線程互斥是指某一資源同上=時只允許一個訪問者對其進行訪問,具有唯一性和排它性猜揪。
  • 互斥無法限制訪問者對資源的訪問順序惭墓,即訪問是無序的。因此需要加上互斥鎖來進行順利訪問而姐,最具代表性的就是買票系統(tǒng)。
  • NSLock 類能協(xié)助完成互斥操作划咐。
#pragma mark -- 買火車票的線程鎖實現(xiàn)
- (void)tickets
{
    // 初始化票數(shù)
    _totalTickets = 100;
    
    // 初始化剩余票數(shù)
    _subTickets = 100;
    
    // 初始化線程鎖
    self.lock = [[NSLock alloc] init];
    
    // 先創(chuàng)建兩個并行隊列
    dispatch_queue_t queue1 = dispatch_queue_create("火車站", DISPATCH_QUEUE_CONCURRENT);
    // 給火車站,添加賣票的任務(wù)
    dispatch_async(queue1, ^{
        // 賣票
        [self saleTickets:queue1];
    });
    
    // 創(chuàng)建12306隊列
    dispatch_queue_t queue2 = dispatch_queue_create("com.12306.www", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue2, ^{
        // 賣票
        [self saleTickets:queue2];
    });
}

#pragma mark -- 售票方法
- (void)saleTickets:(dispatch_queue_t)queue
{
    // 循環(huán)賣票
    while (_subTickets > 0) {
        // 添加鎖
        // 線程鎖 跟自動釋放池 使用的方法差不多
        // 中間夾的部分,是鎖的內(nèi)容
        [self.lock lock];
        
        // 咱們要知道,是哪個隊列來賣票
        // 通過隊列表示符,得到
        // dispatch_queue_get_label 得到隊列的標(biāo)識符
        char *name = (char *) dispatch_queue_get_label(queue);
        NSString *str = [NSString stringWithUTF8String:name];
        NSLog(@"%@ 還剩%ld張票", str, _subTickets);
        
        // 來一回,減少一張
        _subTickets--;
        
        // 鎖結(jié)束
        [self.lock unlock];
    }
}

group任務(wù)

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue2 = dispatch_queue_create("com.sinosoft.queue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue2, ^{
        for (int i = 0; i<5; i++) {
            NSLog(@"1======%d===%@", i, [NSThread currentThread]);
        }
    });
    dispatch_group_async(group, queue2, ^{
        for (int i = 0; i<5; i++) {
            NSLog(@"2======%d===%@", i, [NSThread currentThread]);
        }
    });
    dispatch_group_notify(group, queue2, ^{
     NSLog(@"組1和組2都完成了=========%@", [NSThread currentThread]);
    });
   long isCompleted =  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"%ld", isCompleted);
    /*
     dispatch_group_wait方法是一個很有用的方法拴念,它的完整定義如下:
     
     dispatch_group_wait(group: dispatch_group_t, _ timeout: dispatch_time_t) -> Int
     
     第一個參數(shù)表示要等待的group钧萍,第二個則表示等待時間。返回值表示經(jīng)過指定的等待時間政鼠,屬于這個group的任務(wù)是否已經(jīng)全部執(zhí)行完风瘦,如果是則返回0,否則返回非0公般。
     
     第二個dispatch_time_t類型的參數(shù)還有兩個特殊值:DISPATCH_TIME_NOW和DISPATCH_TIME_FOREVER万搔。
     
     前者表示立刻檢查屬于這個group的任務(wù)是否已經(jīng)完成,后者則表示一直等到屬于這個group的任務(wù)全部完成官帘。
     */
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瞬雹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子刽虹,更是在濱河造成了極大的恐慌酗捌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涌哲,死亡現(xiàn)場離奇詭異胖缤,居然都是意外死亡,警方通過查閱死者的電腦和手機阀圾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門哪廓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人初烘,你說我怎么就攤上這事涡真。” “怎么了账月?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵综膀,是天一觀的道長。 經(jīng)常有香客問我局齿,道長剧劝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任抓歼,我火速辦了婚禮讥此,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谣妻。我一直安慰自己萄喳,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布蹋半。 她就那樣靜靜地躺著他巨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上染突,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天捻爷,我揣著相機與錄音,去河邊找鬼份企。 笑死也榄,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的司志。 我是一名探鬼主播甜紫,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼骂远!你這毒婦竟也來了囚霸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤吧史,失蹤者是張志新(化名)和其女友劉穎邮辽,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贸营,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡吨述,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了钞脂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揣云。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖冰啃,靈堂內(nèi)的尸體忽然破棺而出邓夕,到底是詐尸還是另有隱情,我是刑警寧澤阎毅,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布焚刚,位于F島的核電站,受9級特大地震影響扇调,放射性物質(zhì)發(fā)生泄漏矿咕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一狼钮、第九天 我趴在偏房一處隱蔽的房頂上張望碳柱。 院中可真熱鬧,春花似錦熬芜、人聲如沸莲镣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瑞侮。三九已至的圆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間区岗,已是汗流浹背略板。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工毁枯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留慈缔,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓种玛,卻偏偏與公主長得像藐鹤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赂韵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

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