iOS開發(fā)網(wǎng)絡(luò)多線程之多線程

iOS開發(fā)筆記-多線程01

理論部分:

一.進(jìn)程

1)概念:是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,每個(gè)進(jìn)程的存儲(chǔ)空間是獨(dú)立的(終端中輸入top可以查看進(jìn)程,按Q退出)

P.S.進(jìn)程與應(yīng)用程序的差別:進(jìn)程是有狀態(tài)的,正在執(zhí)行的

2)進(jìn)程是CPU進(jìn)行資源分配與調(diào)度的基本單位

二.線程

1)線程是CPU調(diào)度的基本單位(1個(gè)進(jìn)程想執(zhí)行任務(wù),必須要有線程 ),一個(gè)進(jìn)程中所有的任務(wù)都在線程中執(zhí)行

2)一個(gè)線程中的任務(wù)的執(zhí)行是串行的

P.S.進(jìn)程與線程的辨析

1)一個(gè)程序可以對(duì)應(yīng)多個(gè)進(jìn)程,一個(gè)進(jìn)程中可以有多個(gè)線程,但至少要有一個(gè)線程,線程是進(jìn)程的一條路徑

2)同一個(gè)進(jìn)程內(nèi)的線程共享進(jìn)程的資源

3)進(jìn)程可以分配資源,而線程不可以

3)多線程并發(fā)執(zhí)行:其實(shí)是CPU快速地在多個(gè)線程之間調(diào)度(只是假象,因?yàn)橥粫r(shí)間,CPU只能處理一條線程),建議開辟線程數(shù)量為3-5條

4)主線程:也稱為UI線程,作用是:1.顯示\刷新UI界面 2.處理UI事件 (所有和UI相關(guān)的操作都需要在主線程中執(zhí)行)

打印時(shí),若顯示的number等于1,則為主線程,其他數(shù)字,則為子線程

P.S.主線程注意點(diǎn)

1.不要將耗時(shí)的任務(wù)放在主線程

2.UI刷新操作要放在主線程中

3.耗時(shí)的操作放在子線程中(后臺(tái)線程)

三.pthread


說(shuō)明:pthread的基本使用(需要包含頭文件)
    //使用pthread創(chuàng)建線程對(duì)象
    pthread_t thread;
    //使用pthread創(chuàng)建線程
    //第一個(gè)參數(shù):線程對(duì)象地址
    //第二個(gè)參數(shù):線程屬性
    //第三個(gè)參數(shù):指向函數(shù)的指針
    //第四個(gè)參數(shù):傳遞給該函數(shù)的參數(shù)
    pthread_create(&thread, NULL, run, NULL);

四.NSThread

1.一個(gè)NSThread代表一條線程

2.優(yōu)先級(jí)高,cpu調(diào)用的概率高(影響的也是CPU調(diào)度到的概率)


1)NSThread創(chuàng)建線程的四種方式
        //第一種創(chuàng)建線程的方式:alloc initWithTarget.
        //特點(diǎn):需要手動(dòng)開啟線程,可以拿到線程對(duì)象進(jìn)行詳細(xì)設(shè)置
            //創(chuàng)建線程
            /*
             第一個(gè)參數(shù):目標(biāo)對(duì)象
             第二個(gè)參數(shù):選擇器,線程啟動(dòng)要調(diào)用哪個(gè)方法
             第三個(gè)參數(shù):前面方法要接收的參數(shù)(最多只能接收一個(gè)參數(shù),沒有則傳nil)
             */
            NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"hmx"];
             //啟動(dòng)線程
            [thread start];

        //第二種創(chuàng)建線程的方式:分離出一條子線程
        //特點(diǎn):自動(dòng)啟動(dòng)線程,無(wú)法對(duì)線程進(jìn)行更詳細(xì)的設(shè)置
            /*
             第一個(gè)參數(shù):線程啟動(dòng)調(diào)用的方法
             第二個(gè)參數(shù):目標(biāo)對(duì)象
             第三個(gè)參數(shù):傳遞給調(diào)用方法的參數(shù)
             */
            [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是分離出來(lái)的子線程"];

        //第三種創(chuàng)建線程的方式:后臺(tái)線程
        //特點(diǎn):自動(dòng)啟動(dòng)線程,無(wú)法進(jìn)行更詳細(xì)設(shè)置
            [self performSelectorInBackground:@selector(run:) withObject:@"我是后臺(tái)線程"];
        
        //第四種創(chuàng)建線程方法:alloc init
        //新建一個(gè)類 繼承自NSThread,重寫內(nèi)部的main方法來(lái)封裝任務(wù)
            NSThread *thread = [[NSThread alloc]init];
            //啟動(dòng)線程
            [thread start];

    2)設(shè)置線程的屬性
        //設(shè)置線程的名稱
        thread.name = @"線程A";

        //設(shè)置線程的優(yōu)先級(jí),注意線程優(yōu)先級(jí)的取值范圍為0.0~1.0之間,1.0表示線程的優(yōu)先級(jí)最高,如果不設(shè)置該值,那么理想狀態(tài)下默認(rèn)為0.5
        thread.threadPriority = 1.0;

3.線程狀態(tài)相關(guān)問(wèn)題:

1.線程的各種狀態(tài):新建-就緒-運(yùn)行-阻塞-死亡
2.當(dāng)線程的任務(wù)執(zhí)行完畢之后就銷毀了
3.當(dāng)線程放入可調(diào)度線程池中,CPU才會(huì)調(diào)度
4.線程已經(jīng)死了,是不能再重新打開的
5.當(dāng)線程解除阻塞狀態(tài)時(shí),會(huì)進(jìn)入就緒狀態(tài),而不是運(yùn)行狀態(tài)

4.線程安全相關(guān)問(wèn)題:

    1. 互斥鎖:@synchronized(self){//需要鎖定的代碼}(推薦使用self)
    2. 前提條件:多個(gè)線程可能會(huì)訪問(wèn)同一塊資源
    3. 注意點(diǎn):1)要注意加鎖的位置
             2)鎖對(duì)象必須是對(duì)象,且全局唯一
             3)加上互斥鎖之后,就會(huì)使線程同步(即線程永遠(yuǎn)都是按一個(gè)順序調(diào)用,例:一開始是2->1->3的順序,之后依舊為2->1->3的順序)
             4)線程是需要消耗性能的
    4. 專業(yè)術(shù)語(yǔ)-線程同步
    5. 原子和非原子屬性(是否對(duì)setter方法加鎖)
       atomic 線程安全,需要消耗大量資源
       nonatomic 非線程安全,適合內(nèi)存小的移動(dòng)設(shè)備  (開發(fā)中聲明為這個(gè))

5.計(jì)算代碼段間的執(zhí)行時(shí)間


        //第一種方法
            NSDate *start = [NSDate date];
            //2.根據(jù)url地址下載圖片數(shù)據(jù)到本地(二進(jìn)制數(shù)據(jù))
            NSData *data = [NSData dataWithContentsOfURL:url];

            NSDate *end = [NSDate date];
            NSLog(@"第二步操作花費(fèi)的時(shí)間為%f",[end timeIntervalSinceDate:start]);

        //第二種方法
            CFTimeInterval start = CFAbsoluteTimeGetCurrent();
            NSData *data = [NSData dataWithContentsOfURL:url];

            CFTimeInterval end = CFAbsoluteTimeGetCurrent();
            NSLog(@"第二步操作花費(fèi)的時(shí)間為%f",end - start);

6.回到主線程刷新UI

    //4.1 第一種方式
        [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
    //4.2 第二種方式
        [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
    //4.3 第三種方式
        [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
    }

五.PCD

1.GCD基本知識(shí)

1) 兩個(gè)核心概念:隊(duì)列和任務(wù):
    隊(duì)列:用來(lái)存放任務(wù)(決定在哪個(gè)線程執(zhí)行任務(wù))
    任務(wù):執(zhí)行什么操作
2) 同步函數(shù)和異步函數(shù)的區(qū)別:
    同步:1.只能在當(dāng)前線程中執(zhí)行,不具備開啟新線程的能力
        2.執(zhí)行任務(wù)的方式:當(dāng)執(zhí)行到我時(shí)必須等我執(zhí)行完才能執(zhí)行后面的任務(wù)
    異步:1.可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
        2.執(zhí)行任務(wù)的方式:可以不用等我執(zhí)行完畢,就可以直接執(zhí)行后面的任務(wù)
3)  串行隊(duì)列:1.取出一個(gè)任務(wù)后,等到該任務(wù)執(zhí)行完畢之后,接著去第二個(gè)任務(wù)
            2.創(chuàng)建方式:a.自己創(chuàng)建  b.主隊(duì)列
    并行隊(duì)列:1.取出一個(gè)任務(wù)后,接著執(zhí)行第二個(gè)任務(wù)
            2.創(chuàng)建方式:a.自己創(chuàng)建  b.全局并發(fā)隊(duì)列
    主隊(duì)列:1.在安排任務(wù)的時(shí)候,會(huì)先檢查主線程的狀態(tài),如果主線程忙,那么久暫停調(diào)度直到空閑為止
          2.想要實(shí)現(xiàn)控制系統(tǒng)開幾條線程時(shí),只需要控制創(chuàng)建幾個(gè)隊(duì)列
          3.凡是放在主隊(duì)列里的任務(wù)都在主線程完成

2.GCD基本使用【重要】

01 異步函數(shù)+并發(fā)隊(duì)列:開啟多條線程,并發(fā)執(zhí)行任務(wù)
02 異步函數(shù)+串行隊(duì)列:開啟一條線程,串行執(zhí)行任務(wù)
03 同步函數(shù)+并發(fā)隊(duì)列:不開線程,串行執(zhí)行任務(wù)
04 同步函數(shù)+串行隊(duì)列:不開線程,串行執(zhí)行任務(wù)
05 異步函數(shù)+主隊(duì)列:不開線程,在主線程中串行執(zhí)行任務(wù)
06 同步函數(shù)+主隊(duì)列:不開線程,串行執(zhí)行任務(wù)(注意死鎖發(fā)生)
   P.S若當(dāng)前是子線程執(zhí)行的同步函數(shù)加上主隊(duì)列的方式,不會(huì)發(fā)生死鎖
07 注意同步函數(shù)和異步函數(shù)在執(zhí)行順序上面的差異

3.GCD的線程間通信:


        //0.獲取一個(gè)全局的隊(duì)列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

        //1.先開啟一個(gè)線程,把下載圖片的操作放在子線程中處理
        dispatch_async(queue, ^{

        //2.下載圖片
            NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];

            NSLog(@"下載操作所在的線程--%@",[NSThread currentThread]);

        //3.回到主線程刷新UI
            dispatch_async(dispatch_get_main_queue(), ^{
               self.imageView.image = image;
               //打印查看當(dāng)前線程
                NSLog(@"刷新UI---%@",[NSThread currentThread]);
            });

        });

4.GCD相關(guān)注意點(diǎn)

1. 開線程的兩個(gè)條件:1.必須是異步函數(shù) 2.必須不是主隊(duì)列(主線程)
2. 放到主隊(duì)列里的任務(wù),必須要在主線程中執(zhí)行(不一定在當(dāng)前線程中執(zhí)行,即即使在子線程中調(diào)用了主隊(duì)列,還是子主線程中調(diào)用)

5.GCD常用函數(shù):

   
   1)柵欄函數(shù)(控制任務(wù)的執(zhí)行順序)
        dispatch_barrier_async(queue, ^{
            NSLog(@"--dispatch_barrier_async-");
        });
    /*
    P.S 注意點(diǎn):1.柵欄函數(shù)可以控制線程的執(zhí)行順序
              2.柵欄函數(shù)不能使用全局并發(fā)隊(duì)列
              3.柵欄函數(shù)在執(zhí)行時(shí)是獨(dú)占的
              4.dispatch_barrier_async == dispatch_async  這兩個(gè)是等同的
    */
    
    2)延遲執(zhí)行(延遲·控制在哪個(gè)線程執(zhí)行)
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"---%@",[NSThread currentThread]);
        });
    /*
    Q:GCD的延遲執(zhí)行 是先等2秒再提交 OR 先提交再等2秒?
    A:先等2秒再提交(延遲提交)  因?yàn)槿蝿?wù)提交到隊(duì)列里就不好控制了
    */

    3)一次性代碼(注意不能放到懶加載)
        -(void)once
        {
            //整個(gè)程序運(yùn)行過(guò)程中只會(huì)執(zhí)行一次
            //onceToken用來(lái)記錄該部分的代碼是否被執(zhí)行過(guò)
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{

                NSLog(@"-----");
            });
        }
    /*
    一次性代碼的特點(diǎn):1.整個(gè)程序運(yùn)行過(guò)程中只會(huì)執(zhí)行一次
                      2.它本身是線程安全的
    */

    4)快速迭代(開多個(gè)線程并發(fā)完成迭代操作)
           dispatch_apply(subpaths.count, queue, ^(size_t index) {
        });
     /*
     快速迭代:多個(gè)線程(子線程與主線程一起工作的)一起并發(fā)執(zhí)行任務(wù)的--->對(duì)順序沒有要求時(shí)使用
     Q:快速迭代若不是從0開始,怎么處理
     A:初始值若不是0,則不需要使用GCD的快速迭代,使用for循環(huán)即可
     */
    
    5)隊(duì)列組(同柵欄函數(shù))--->調(diào)度組
        //創(chuàng)建隊(duì)列組
        dispatch_group_t group = dispatch_group_create();
        //隊(duì)列組中的任務(wù)執(zhí)行完畢之后,執(zhí)行該函數(shù)
        dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);//這個(gè)方法本身也是異步的

    /*
    方便管理一個(gè)組內(nèi)的操作
    */

    6)進(jìn)入群組和離開群組
        dispatch_group_enter(group);//執(zhí)行該函數(shù)后,后面異步執(zhí)行的block會(huì)被gruop監(jiān)聽
        dispatch_group_leave(group);//異步block中,所有的任務(wù)都執(zhí)行完畢,最后離開群組
       /*
       注意:dispatch_group_enter|dispatch_group_leave必須成對(duì)使用
            當(dāng)需要監(jiān)聽多個(gè)任務(wù)時(shí),則重復(fù)寫一組即可
       */
       //死等方法:知道隊(duì)列組中所有都執(zhí)行完畢之后才過(guò)掉該方法(同步)
       //diepatch_group_wait(group,DISPATCH_TIME_FOREVER)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 從哪說(shuō)起呢? 單純講多線程編程真的不知道從哪下嘴。。 不如我直接引用一個(gè)最簡(jiǎn)單的問(wèn)題,以這個(gè)作為切入點(diǎn)好了 在ma...
    Mr_Baymax閱讀 2,818評(píng)論 1 17
  • 什么是進(jìn)程? 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序。 每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存...
    珍此良辰閱讀 1,270評(píng)論 1 5
  • 歡迎大家指出文章中需要改正或者需要補(bǔ)充的地方,我會(huì)及時(shí)更新,非常感謝。 一. 多線程基礎(chǔ) 1. 進(jìn)程 進(jìn)程是指在系...
    xx_cc閱讀 7,234評(píng)論 11 70
  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了!去的時(shí)候我都想好了最壞的可能(胃癌),之前在網(wǎng)上查的癥狀都很相似。...
    Dely閱讀 9,265評(píng)論 21 42
  • Object C中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?如果想延時(shí)執(zhí)行代碼、方法又是什么? 1...
    AlanGe閱讀 1,777評(píng)論 0 17