多线程 NSThread GCD NSOperation 基础篇


先总说一下, ios 实现多线程 有4种方式, 其中比较常用的有  1. NSThread  2. GCD 3. NSOperation 三种方式. 
GCD 全称: Grand Central Dispatch  是OS10.6  首次推出, iOS4.0 引入到iOS系统中
NSOperation是对GCD 的封装.

 一个线程被创建后会要放到线程池中才会被CPU 执行(有些会自动放到线程池中如creatNSThread2,有些需要手动放到线程池中如: creatNSThread1), 

    在线程池中的线程有三种状态 1,就绪状态(等待CPU执行), 2 ,运行状态(CPU正在执行) 3, 阻塞状态(seelp或者线程锁)




下面通过代码 逐一介绍. 
一   1. NSThread

/// 第一种 使用 NSThread开启线程的方法

-(void)creatNSThread1{

    // NSThread 是一个对象,alloc 一次开启一个线程

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

    // 线程开启后需要 手动启动线程

    // 生命周期不用程序员管理, 在线程任务结束后,系统会自动释放 thread

    [thread start];

    thread.name =@"new";

    // 查看是否是主线程

    // [thread isMainThread];

    //  [NSThread isMainThread];

}


/// 第二种 使用 NSThread开启线程的方法

-(void)creatNSThread2{

    // 这个方式不需要手动开启, 也不需要管理生命周期,也没有返回不能像第一种方法一样给线程设置名字

    [NSThreaddetachNewThreadSelector:@selector(run:)toTarget:selfwithObject:@"aaa"];

}

/// 第三种 使用 NSThread开启线程的方法 .隐式创建

-(void)creatNSThread3{

    [selfperformSelectorInBackground:@selector(run:)withObject:self];

}

//  线程之间的通信

-(void)threadTalk{

    // 从子线程回到主线程,多用于子线程中下载完毕,或者耗时操作完成,回到主线程更新UI,

    // waitUntilDone, YES :等待主线程上操作完成, 在继续执行本线程的事件, NO:不等待

    [selfperformSelectorOnMainThread:@selector(run:)withObject:selfwaitUntilDone:YES];

    // 从本线程回到指定线程

    [selfperformSelector:@selector(run:)onThread:[NSThreadmainThread] withObject:@""waitUntilDone:YES];

}

/*

 

   线程锁(线程同步,多条线程在同一条线上顺序执行,多线程默认是异步的) :为了解决多线程中多个线程访问同一个资源的问题

    例如 A B线程要对 C 资源做操作

    如果A先对 C操作,会先对C 加锁,在读取C的数据,经过线程A 对数据更改后,写入C 完成,解锁.

    在加锁期间,线程B 不能访问到C的数据

 */


-(void)run1{

    while (1) {

        @synchronized (self) {

           

        }

    }

}

 // 阻塞线程

        [NSThreadsleepForTimeInterval:2];

 // 强制退出线程

            [NSThreadexit];


//===================================================

2 GCD

    // GCD  主要任务 队列

    //  先进先出,后进后出原则

/*

   1, GCD 有两种常见的执行任务的方式,

   2, 队列 GCD并发队列(具备开启多个线程的能力),串行队列(不具备开启线程的能力).

      :异步执行 选择的是并队列时,才具有开启线程的能力

    任务:决定具不具备开启线程的能力, 会不会阻塞当前线程

    队列:影响的是 是否允许多个任务同时执行.

   同步任务 +主队列 会锁死.一定不要这样写.


// 同步任务 +主队列 会锁死.一定不要这样写.



 */

-(void)creatGCD1{

    // 第一中,同步执行任务的方式

 dispatch_sync(dispatch_get_main_queue(), ^{

     // 这里是要执行的任务

 });

   // 第二种,异步执行任务的方式

  dispatch_async(dispatch_get_main_queue(), ^{

      // 这里是要执行的任务

  });

    // 同步和异步的区别是:同步是在当前的线程中执行任务, 不开线程, 会阻塞当前线程 异步会开启新的线程, 不会阻塞当前线程

}

// 创建一个并发/串行队列 + 异步

-(void)creatGCD2{

   // 第一个参数:队列的名称,标签第二个参数: 表明是串行(DISPATCH_QUEUE_SERIAL)还是并发(DISPATCH_QUEUE_CONCURRENT)

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

   // dispatch_get_global_queue GCD提供的全局的并发队列

    // 第一个参数表明优先级,第二个参数暂时没有用,

   dispatch_queue_t golbai_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    // 将任务添加到刚刚创建的并发队列中.

    dispatch_async(queue, ^{

        // 会开启前子线程,

    });

}

// 同步任务 +主队列 会锁死.一定不要这样写.

// 原因是 :主队列 是要顺序执行,同步任务, 任务是要先后执行,不能一起执行. 执行creatGCD3是在主队列中执行, 所以要从上往下依次执行,而同步任务也是顺序执行.    也就是说,主队列要求顺序执行 先执行nslog,creatGCD3 在主队列中, nslog代码块也在主队列中, creatGCD3 在代码块之前,所以, 同步任务要求先把 creatGCD3执行完,才能执行 nslog, 导致程序锁死.

-(void)creatGCD3{

  dispatch_sync(dispatch_get_main_queue(), ^{

      // 这里的代码不会被执行,程序被锁死

      NSLog(@"kkkkkkkkk");

  });

}

/*

1.  dispatch_barrier_async 栅栏.可以保证 先执行栅栏之前的任务, 然后在执行栅栏只会的任务,最后执行栅栏之后的任务.

  注意异步任务不能加入到 全局队列,否则 栅栏就没用了.

 

2. 这是一个异步 + 并发队列,会先执行 end 在执行开启线程的工作.

 */

-(void)creatGCD4{


    for (int i =0; i < 10; ++i) {

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

    dispatch_async(queue, ^{

        NSLog(@"111111");

    });

    dispatch_async(queue, ^{

        NSLog(@"222222");

    });

    

    dispatch_barrier_async(queue, ^{

        NSLog(@"barrier");

    });

    dispatch_async(queue, ^{

        NSLog(@"333333");

    });

    dispatch_async(queue, ^{

        NSLog(@"444444");

    });

   }

    NSLog(@"-----  end");

    // 会先执行 打印 111111  222222  再打印  barrier 再打印 333333    444444

    //  但是    111111,  222222并不一定按顺序执行. 333333 444444也不一定按顺序执行.

}

  // 延迟操作

-(void)creatGCD5{

  // 第一种

    [selfperformSelector:@selector(run:)withObject:selfafterDelay:2.0];

  // 第二种

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{       

    });

}

// 单例

-(void)creatGCD6{

    //  单例重写的是 +(instancetype)allocWithZone:(struct _NSZone *)zone 这个封的比较死

    //  一般都是自己写一个 share方法,在内部 [[ alloc] init]方法,做一次性执行

    staticdispatch_once_t once;

    dispatch_once(&once, ^{

        

       // 这里的代码只会执行一次.

    });

}

// 队列组要求 任务一,任务二 两个耗时操作都完成后,执行任务三

-(void)creatGCD7{

    // 创建一个队列组

    dispatch_group_t group =dispatch_group_create();

    

    dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    

    dispatch_group_async(group, queue, ^{

        for (int i =0; i < 100; ++i) {

            NSLog(@"任务一");

        }

    });

    

    dispatch_group_async(group, queue, ^{

        for (int i =0; i < 100; ++i) {

            NSLog(@"任务二");

        }

    });

    

    dispatch_group_notify(group, queue, ^{

        // 当任务组里的任务都完成了,会执行这里

        NSLog(@"任务三");

        

        dispatch_async(dispatch_get_main_queue(), ^{

            // 回到主线程做跟新UI操作

        });

    });

}

// ================================================

 // NSOperation  是对GCD的封装,更加面向对象,主要靠 NSOpetation子类工作

///  NSBlockOperation 开启线程

-(void)creatNSOperation1{

 NSBlockOperation *op = [NSBlockOperationblockOperationWithBlock:^{

     // 第一个任务会在主线程中执行

        NSLog(@"任务一");

 }];

    

    [op addExecutionBlock:^{

        // 当有两个或者连个以上的任务, 会开启子线程,但是第一个任务还是在主线程中执行.

        NSLog(@"任务二");

    }];

    

    [op addExecutionBlock:^{

        // 当有两个或者连个以上的任务, 会开启子线程,但是第一个任务还是在主线程中执行.

        NSLog(@"任务三");

    }];

}

///  NSOperationQueue  + NSInvocationOperation 开启线程

-(void)creatNSOperation2{

    // 创建队列

    NSOperationQueue *queue = [NSOperationQueuenew];

    

    NSOperationQueue *queue2 = [NSOperationQueuenew];

    

    

    // 最大并发数最大并发数= 3 并不是说,在打印时 线程序号不会超过3,而是说在同一时间,最多给这个进程分配三个线程,有可能是刚好别的线程刚执行完, 立马拿来复用,

    // 如果最大并发数 = 1. queue就变成串行队列了.

    queue.maxConcurrentOperationCount =3;

    

    

    // 挂起/暂停  YES : 挂起/暂停  NO : 取消挂起/取消暂停.

    // 当有多个线程在执行,而用户要做一些比较好性能的操作时, 可以先将后台线程挂起,保证用户的流程度,当用户操作完成后, 在取消挂起,

    // 但是当线程已经开始工作了, 不会被挂起,只有还没开始执行的线程会被挂起.

   // queue.suspended = YES;

    

    

    // queue.suspended的区别是cancelAllOperations是取消, 一旦取消就不能回复,需要重新add

    // queue.suspended相同的是,cancelAllOperations也不能中途取消线程.

    // 如果想要取消,线程,建议, 在每一个耗时操作后面自己添加一个判断,if (op.isCancelled == YES){ return; }

    [queue cancelAllOperations];

    

    

    // 创建任务

   NSInvocationOperation *op1 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(run:)object:@"参数"];

    NSInvocationOperation *op2 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(run:)object:@"参数"];

    NSInvocationOperation *op3 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(run:)object:@"参数"];

    NSInvocationOperation *op4 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(run:)object:@"参数"];

    

    NSBlockOperation *bq = [NSBlockOperationblockOperationWithBlock:^{

        NSLog(@"任务一");

    }];

    

    [bq addExecutionBlock:^{

        NSLog(@"任务二");

    }];

    // ------------------

    

    

    // 监听任务完

    [op4 setCompletionBlock:^{

        

        // 这里还是在子线程中, 如果想更新UI要回到主线程中.

        NSLog(@"任务4完成");

    }];

    

    // 设置依赖 op1会等待 op2, op3 执行完成后再执行,这个依赖可以跨队列依赖.不要循环依赖,

    [op1 addDependency:op2];

    [op1 addDependency:op3];

    // --------------

    

    // 只有将任务添加到队列中,任务才开始执行. 会开启子线程

    [queue addOperation:op1];

    [queue addOperation:op2];

    [queue2 addOperation:op3];

    [queue2 addOperation:op4];

    

    // NSBlockOperation添加到任务中也能开启线程

    [queue addOperation:bq];


}

// 线程间的通信

-(void)creatNSOperation3{

  // 创建队列

    NSOperationQueue *queue = [NSOperationQueuenew];

    

    // 将任务添加到队列中

    [queue addOperationWithBlock:^{

        NSLog(@"做耗时操作");

        // 耗时操作完后回到主线程.

        [[NSOperationQueuemainQueue]addOperationWithBlock:^{

            NSLog(@"回到主线程");

        }];

    }];


}








你可能感兴趣的:(多线程)