【iOS】第02讲 多线程NSThread/GCD/RunLoop/NSTimer/Socket数据传输

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Nanhu2012/article/details/75025670

一、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{

    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(Run:) object:nil];

    

    [self.thread start];


}


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

    

    if([Network Create] != true)

    {

        return ;

    }

    

    //创建子线程对应的RunLoop

    [NSRunLoop currentRunLoop];

    

    [self timer];

    

    [[NSRunLoop currentRunLoop] run];



}


-(void)run{

    [Network Run:nil];

    NSLog(@"network loop run 1");

}


-(void)run2{

    [Network Run:nil];

    NSLog(@"network loop run 2");

}


-(void)timer{

    //1.创建定时器

    self.timer1 = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(run) userInfo:nil repeats:YES];

    self.timer2 = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(run2) userInfo:nil repeats:YES];

    

    //2.添加定时器到RunLoop

    /*

     第一个参数:定时器

     第二个参数:RunLoop的运行模式

     */

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

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

    

    //凡是添加到CommonModes中的事件都会被同时打上common标签的运行模式上

    [[NSRunLoop currentRunLoop] addTimer:self.timer1 forMode:NSRunLoopCommonModes];

    [[NSRunLoop currentRunLoop] addTimer:self.timer2 forMode:NSRunLoopCommonModes];

    

//    [self.timer1 setFireDate:[NSDate distantFuture]];

    [self.timer2 setFireDate:[NSDate distantFuture]];

    

//    [self.timer1 setFireDate:[NSDate distantPast]];

}


-(void)ActiveTimer1{

//    [self.timer1 setFireDate:[NSDate distantFuture]];

    [self.timer2 setFireDate:[NSDate distantFuture]];

    

    [self.timer1 setFireDate:[NSDate distantPast]];

}


-(void)ActiveTimer2{

    [self.timer1 setFireDate:[NSDate distantFuture]];

//    [self.timer2 setFireDate:[NSDate distantFuture]];

    

    [self.timer2 setFireDate:[NSDate distantPast]];

}


@end




猜你喜欢

转载自blog.csdn.net/Nanhu2012/article/details/75025670