1. 程式人生 > >【iOS】第02講 多執行緒NSThread/GCD/RunLoop/NSTimer/Socket資料傳輸

【iOS】第02講 多執行緒NSThread/GCD/RunLoop/NSTimer/Socket資料傳輸

一、NSThread

      1.1 基本使用

-(void) createThread{          

    //NSThread

    //1.建立執行緒

    /*

     第一個引數:目標物件 self

     第二個引數:方法選擇器呼叫的方法

     第三個引數:前面方法需要傳遞的引數

     */

    NSThread *thread = [[NSThreadalloc]initWithTarget:selfselector:@selector(run:)object:@"abc"];

    

    //2.啟動執行緒

    [thread start];

}


-(void)run:(NSString *)param{

    NSLog(@"---run---%@",[NSThreadcurrentThread]);

}


二、GCD簡介

      2.1 什麼是 GCD

            全稱是 Grand Central Dispatch,可以譯為“牛逼的中樞排程起“。

            純C語言,提供了非常多強大的函式

      2.2 GCD的優勢

            GCD是蘋果公司為多核的並行運算提出的解決方案。

            GCD會自動利用更多的CPU核心(比如雙核、四核)

            GCD會自動管理執行緒的生命週期(建立執行緒、排程任務、銷燬執行緒)

            程式設計師只需要告訴GCD想要執行什麼任務,不需要編寫任何執行緒管理程式碼


三、任務和佇列

    3.1 GCD中有2個核心概念

        > 任務: 執行什麼操作

        > 佇列: 用來存放任務

    3.2 GCD的使用就2個步驟

        > 定製任務

           確定想做的事情

        > 將任務新增到佇列中

           GCD會自動將佇列中的任務取出,放到對應的執行緒中執行

           任務的取出遵循佇列的FIFO原則:先進先出,後進後出

     3.3 執行任務

         > GCD中有2個用來執行任務的常用函式

             >用同步的方式執行任務

             dispatch_sync(dispatch_queue_t queue,dispatch_block_t block);

              queue - 佇列

              block - 任務

             >用非同步的方式執行任務

             dispatch_async(dispatch_queue_t queue,dispatch_block_t block);

           

             >同步和非同步的區別

              同步:只能在當前執行緒中執行任務,不具備開啟新執行緒的能力

              非同步:可以在新的執行緒中執行任務,具備開啟新執行緒的能力

      3.4 佇列的型別

          GCD的佇列可以分為2大型別

          併發佇列 (Concurrent Dispatch Queue)

             可以讓多個任務併發執行,自動開啟多個執行緒同時執行任務

             併發功能只有在非同步(dispatch_async)函式下才有效

          序列佇列 (Serial Dispatch Queue)

              讓任務一個接著一個地執行,一個執行完畢後,再執行下一個任務

       

//非同步函式+併發佇列

-(void)asyncConcurrent{

    //1.建立佇列

    /* 

     第一個引數:標籤,用來區分佇列

     第二個引數:佇列的型別,併發/序列

     */

    dispatch_queue_t queue =dispatch_queue_create("first",DISPATCH_QUEUE_CONCURRENT);

    

    //2.封裝任務

    /*

     第一個引數:佇列

     第二個引數:要執行的任務

     */

    dispatch_async(queue,^{

        NSLog(@"download1---%@",[NSThreadcurrentThread]);

    });

}


四、RunLoop

    4.1 什麼是RunLoop

       從字面意思看 是 執行迴圈 跑圈

       基本作用:

          保持程式執行

          處理app中的各種事件(比如觸控事件、定時器事件、Selector事件)

          節省CPU資源,提高程式效能:該做事時做事,該休息時休息

          BOOL running=YES;

          do{

             //...

          }while(running);

          有RunLoop的情況下,由於main函式裡面啟動了RunLoop

          所以程式並不會馬上退出,保持執行狀態


int main(int argc,char * argv[]) {

    @autoreleasepool {

        returnUIApplicationMain(argc, argv,nil,NSStringFromClass([AppDelegateclass]));

    }

}

          UIApplicationMain函式內部啟動了一個RunLoop

          所以UIApplicationMain一直沒有返回,保持了程式的持續執行

          這個預設啟動的RunLoop是跟主執行緒關聯的


     4.2 RunLoop物件

          iOS中有2套API來訪問和使用RunLoop

          1> Foundation

                NSRunLoop


           2> CoreFoundation

                CFRunLoopRef


          NSRunLoop和CFRunLoopRef都代表著RunLoop物件

          NSRunLoop是基於CFRunLoopRef的一層OC包裝,所以要了解RunLoop內部結構,

          需要多研究CFRunLoopRef層面的API(Core Foundation層面)


     4.3 RunLoop官方資料

         RunLoop官方文件地址

         

     4.4 RunLoop與執行緒

         每條執行緒都有唯一的一個與之對應的RunLoop物件

         主執行緒的RunLoop已經自動建立好了,子執行緒的RunLoop需要主動建立

         RunLoop在第一次獲取時建立,線上程結束時銷燬

         1> 獲得RunLoop物件

            >Foundation

               [NSRunLoop currentRunLoop];  //獲得當前執行緒的RunLoop物件

               [NSRunLoop mainRunLoop]; //獲得主執行緒的RunLoop物件

            >Core Foundation

               CFRunLoopGetCurrent(); //獲得當前執行緒的RunLoop物件

               CFRunLoopGetMain();//獲得主執行緒的RunLoop物件

          2> 子執行緒RunLoop建立

    NSThread *thread = [[NSThreadalloc]initWithTarget:selfselector:@selector(run:)object:@"abc"];

    

    [thread start];


-(void)run:(NSString *)param{

    NSLog(@"---run---%@",[NSThreadcurrentThread]);

    

    //建立子執行緒對應的RunLoop

    NSLog(@"%@",[NSRunLoopcurrentRunLoop]);

}

     4.5 RunLoop相關類

        Core Foundation中關於RunLoop的5個類

        1> CFRunLoopRef

        2> CFRunLoopModeRef

        3> CFRunLoopSourceRef

        4> CFRunLoopTimerRef

        5> CFRunLoopObserverRef

        

        一個 RunLoop裡可以有多個Mode

        一個Mode裡至少要有一個Source或者一個Timer

        RunLoop只能選擇一種Mode執行

        如果需要切換Mode,只能退出Loop,再重新指定一個Mode進入

        這樣做的好處是為了分隔開不同組的Source/Timer/Observer,讓其互不影響


        系統預設註冊了5個Mode:

           kCFRunLoopDefaultMode: App的預設Mode,通常主執行緒在這個模式下執行

           UITrackingRunLoopMode: 介面追蹤Mode,用於ScrollView追蹤觸控滑動,

                 保證介面滑動時不受其他Mode影響

           UIInitializationRunLoopMode: 在剛啟動 App時進入的第一個Mode,啟動完成後

                   就不再使用

           GSEventReceiveRunLoopMode: 接受系統事件的內部Mode,通常用不到

           kCFRunLoopCommonModes: 這一個佔位用的Mode,不是一種真正的Mode

 

{

    [selftimer];  

}


-(void)timer{

    //1.建立定時器

    NSTimer* timer = [NSTimertimerWithTimeInterval:2.0target:selfselector:@selector(run)userInfo:nilrepeats:YES];

    

    //2.新增定時器到RunLoop

   /*

     第一個引數:定時器

     第二個引數:RunLoop的執行模式

     */

//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

    

    //凡是新增到CommonModes中的事件都會被同時打上common標籤的執行模式上

    [[NSRunLoopcurrentRunLoop]addTimer:timerforMode:NSRunLoopCommonModes];

}


-(void)run{

    NSLog(@"---run---%@",[NSThreadcurrentThread]);

    

    //建立子執行緒對應的RunLoop

    NSLog(@"%@",[NSRunLoopcurrentRunLoop]);

    

    //啟動RunLoop

    [[NSRunLoopcurrentRunLoop]run];

    //當前模式

    NSLog(@"Mode: %@",[NSRunLoopcurrentRunLoop].currentMode);

}

 

五、Socket資料傳輸

   要實現基於socket的資料傳輸,需要實現兩個類Network和NetworkThread

   網路單獨放進一個執行緒裡

   同時配合timer進行資料接收頻率的調整

      timer1預設是0.1秒間隔觸發,空閒時刻啟用

      timer2則是在有網路資料到達,0秒間隔迅速讀取資料

   實現之後,在AppDelegate的application函式裡面

    呼叫即可開啟網路收發資料功能

    具體的socket/select相關原理部分,可以參考【Linux C/C++】TCP

    //Network setup

    [Network Init];


   5.1 Network類

Network.h:

#ifndef Network_h

#define Network_h


#import <sys/socket.h>

#import <netinet/in.h>

#import <arpa/inet.h>

#import <unistd.h>


@interface Network : NSObject



+(void)Init;


+(void)Run:(void*) data;


+(bool)Create;


+(bool)Connect;


+(void)Select;


+(void)ReadStream;


+(void)SendMessage:(char*)data :(ssize_t)len;


+(void)Close;


@end


#endif /* Network_h */


Network.mm

#import <Foundation/Foundation.h>

#import "Network.h"

#import "NetworkThread.h"

#import "MessageQueue.h"

#import <fcntl.h>


@interface Network()


@end


static int sock = -1;

static bool connected = false;

static NetworkThread* thread = 0;


static struct fd_set fds,fdsErr;

static struct timeval timeout={0,0}; 

static int maxfdp = 1;

static bool Timer1Active = true;


char buffer[1024];


@implementation Network


+(void) Init{

    thread = [[NetworkThread alloc] init];

    

    [thread CreateThread];

}


+(void) Run:(void*) data{

    if(false == connected)

    {

//        int flags = fcntl(sock, F_GETFL, 0);

//        fcntl(sock, F_SETFL, flags & ~O_NONBLOCK);

        

        [Network Connect];

    }

    

    if(connected)

    {

        [Network Select];

        

    }

}


+(bool) Create{

    sock = socket(AF_INET,SOCK_STREAM,0);

    

    if(-1 == sock)

    {

        NSLog(@"socket create err");

        return false;

    }

    

    return true;

}


+(bool) Connect{


    struct sockaddr_in serv_addr;

    

    memset(&serv_addr, 0, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;

    serv_addr.sin_addr.s_addr = inet_addr("119.23.71.163");

    serv_addr.sin_port = htons(18888);

    

    int r = connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    

    if (-1 == r)

     {

         printf("%m\n");

         connected = false;

         NSLog(@"network connect fail");

         return false;

     }

    

    

    int flags = fcntl(sock, F_GETFL, 0);

    fcntl(sock, F_SETFL, flags | O_NONBLOCK);

    

    NSLog(@"network connected");

    

    connected = true;

    return true;

}


+(void)Select{


    FD_ZERO(&fds); //每次迴圈都要清空集合,否則不能檢測描述符變化

    FD_ZERO(&fdsErr);

    FD_SET(sock,&fds); //新增描述符

    FD_SET(sock,&fdsErr);

    

    maxfdp = sock+1;

    switch(select(maxfdp,&fds,NULL,&fdsErr,&timeout))   //select使用

    {

       case -1:

            exit(-1);

            break; //select錯誤,退出程式

       case 0:

        {

            if(!Timer1Active && send_mq.IsNull())

            {

                Timer1Active = true;

                [thread ActiveTimer1];

            }

        }

            break; //再次輪詢

       default:

        {

            

            if(FD_ISSET(sock,&fdsErr))

            {

                //close fd

                [Network Close];

                

                [Network Create];

                

                return ;

            }

            else if(FD_ISSET(sock,&fds)) //測試sock是否可讀,即是否網路上有資料

            {

                if(Timer1Active)

                {

                    Timer1Active = false;

                    [thread ActiveTimer2];

                }

                //read fd

                [Network ReadStream];

                

                return ;

            }

        }

            break;

    }// end switch

    

    if(!send_mq.IsNull())

    {

        struct Message* msg = send_mq.Pop();

        [Network SendMessage:msg->data :msg->size];

        free_mq.Push(msg);

    }

}


+(void)ReadStream{

    ssize_t len = read(sock,buffer,1024);

    

    if(-1 != len)

    {

        //dispatch this buffer data

        struct Message* msg=0;

        if(free_mq.IsNull())

        {

            msg = new Message;

        }

        else

            msg = free_mq.Pop();

        

        msg->size = len;

        memcpy(msg->data,buffer,len);

        

        global_mq.Push(msg);

        

        return ;

    }

    

    [Network Close];

    [Network Create];

}


+(void)SendMessage:(char*)data :(ssize_t)len{

    while(len > 0)

    {

        ssize_t ret_len = write(sock,data,len > 1024 ? 1024 : len);

        if(-1 != ret_len)

        {

            //

            len -= ret_len ;

            data += ret_len ;

        }

        else

        {

            

            [self Close];

            [self Create];

        }

    

    }

}


+(void)Close{

    close(sock);

    

    connected = false;

}


@end


   5.2 NetworkThread類

NetworkThread.h:

#ifndef NetworkThread_h

#define NetworkThread_h


@interface NetworkThread : NSThread



-(void)CreateThread;


-(void)ActiveTimer1;


-(void)ActiveTimer2;


@end


#endif /* NetworkThread_h */


NetworkThread.mm

#import <Foundation/Foundation.h>

#import "NetworkThread.h"

#import "Network.h"


@interface NetworkThread()


@property NSThread* thread;

@property NSTimer* timer1;

@property NSTimer* timer2;


@end


@implementation NetworkThread


-(void) CreateThread{

相關推薦

iOS02 執行NSThread/GCD/RunLoop/NSTimer/Socket資料傳輸

一、NSThread       1.1 基本使用 -(void) createThread{               //NSThread   &nb

Linux C/C++ 08 執行TCP傳輸檔案/select模型

一、多執行緒    pthread.h    libpthread.so   -lpthread    1.建立多執行緒      1.1 程式碼   &nbs

UE4 02 安卓打包

    安卓打包這個簡直是UE4開發的終極魔鬼問題,原因就是完全不清楚Epic這一部分是怎麼實現的,所以根本沒什麼辦法理解問題產生的根源在哪裡。到目前為止已經兩次重灌Win8.1 64位 企業版,VS2015 32位 企業版,原來的Win7 64位 旗艦版,VS2013 專業

iOS03 檔案讀寫/NSArray/NSData/NSFileManager/NSFileHandle

一、沙盒    模擬器上的沙盒目錄路徑    ~/Library/Application Support/iPhone Simulator/6.0/Applications         &n

iOS01 UIView/UIViewController/UIApplication詳解

一、UIView詳解 Command+Alt+Enter -> 顯示ViewController 按住Ctrl直接把UIView拖到ViewController  1.1 UIView的常見屬性  @property(nonatomic,reado

07單例VS執行

 還是套路問題,一種思想而已,兩種方式   1 dubble check instance   2 static inner class   兩次檢測加類鎖   靜態內部類,其實就是餓漢模式,直接給你就好了   package Concurre

Java基於TCP協議執行伺服器-客戶端互動控制檯聊天室簡例

      前兩天想到一個手機APP專案,使用到藍芽,發現BluetoothSocket和J2EE網路變成的Socket差不多,使用之餘順手寫一個多執行緒伺服器與客戶端互動實現聊天室的一個小例子,方便新人學習網路程式設計模組,期間使用到多執行緒和IO輸入輸出流的

收藏C#中的執行——執行同步基礎

第二部分:執行緒同步基礎 同步要領 下面的表格列展了.NET對協調或同步執行緒動作的可用的工具: 簡易阻止方法 構成 目的 Sleep 阻止給定的時間週期 Join 等待另一個執行緒完成 鎖系統 構成 目的 跨程序?

SpringSpring高階話題-執行-TaskExecutor

分析 在Spring中,通過任務執行器,也就是TaskExecutor來實現多執行緒和併發程式設計。 使用ThreadPoolTaskExecutor可實現一個基於執行緒池的TaskExecutor。 而實際開發中任務一般是非阻礙的,也就是非非同步

UE4 12 FSocket斷線重連

(版權宣告,禁止轉載) 【第03講】 實現了FSocket的連網基礎功能,這一講實現一下 斷線重連 <如果資深前輩發現有理解錯誤,還請不吝指正> <1> 建立Socket,設定阻塞模式(預設創建出來的就是阻塞模式,不用設定)  

UE4 11 HTC Vive裝置安裝使用及手柄控制漫遊

(版權宣告,禁止轉載)     HTC Vive是由HTC與Valve聯合開發的一款VR頭顯(虛擬現實頭戴式顯示器)產品,於2015年3月在MWC2015上釋出。由於有Valve的SteamVR提供的技術支援, 因此在Steam平臺上已經可以體驗利用Vive功能的虛擬現實

UE4 10 Matinee相機過場動畫

(版權宣告,禁止轉載)                                     &

UE4 09 隱藏預設建立的DefaultPawn

(版權宣告,禁止轉載)        場景預設會建立一個ADefaultPawn的物件DefaultPawn,執行起來會看到一個詭異的球,並沒有用到,需要隱藏掉(或者沒用可以Destroy)       &nbs

UE4 08 實現全方位的行走

(版權宣告,禁止轉載)      行走控制元件的渲染實現之後,下一步為其新增Pressed事件響應,具體可以參考UButton裡的實現      Pressed的時候,將WalkWidget的方位資訊傳遞給CharacterPa

UE4 07 實現自定義的行走控制元件

      在圖形業,只有技術是不行的,你要明白我們從事的工作,我們可是在作詩,我們是詩人 - Nvidia創始人黃仁勳(圖形皇帝)       (版權宣告,禁止轉載)      U

UE4 06 碰撞事件響應

(版權宣告,禁止轉載)        今天實現了一下碰撞事件的響應問題,原本感覺陽光明媚,然而當打包到Android的時候,一下掉進了黑暗的深淵,UE4的安卓打包簡直是魔鬼,在真機上跑起來的時候和在電腦上的預覽效果有很大不同,Panda(我喜歡功夫熊貓

UE4 05 發射物方向調整

(版權宣告,禁止轉載)        角色發動技能的時候,需要預定義預設發射方向,如果發現方向不是自己預期的,就需要去做調整,需要用到FTransform,FRotator,FQuat,實際上就是矩陣和四元數的計算問題了。    

UE4 04 隱藏新建工程自帶的操作控制元件

  (版權宣告,禁止轉載)        UE4.14在新建移動平臺的工程之後,啟動會顯示預設的兩個白色圓圈操作控制元件,一個控制預設CameraActor的方向,一個控制移動。當你要自行實現功能的時候,就需要把它們隱藏掉了。 &nb

UE4 15 Slate架構

(版權宣告,禁止轉載) Slate是一種完全自定義的、平臺無關的使用者介面架構,其設計目的是使得構建工具及應用程式(比如虛幻編輯器)的使用者介面或者應用中的使用者介面變得更加有趣、高效。它結合了一種可以輕鬆設計、佈局及風格化元件的宣告式語法,使得可以輕鬆地建立使用者介面並進行迭代開發 &

UE4 14 Triangle的光柵化

(版權宣告,禁止轉載)    為了避免困惑,讓我們簡化定義“光柵化”:判斷一個畫素是否在三角形的內部    在這篇文章,不會討論渲染管線的其它部分,比如深度,模版,混合的實現細節      對於當前的目的,所有