IOS多线程

本文详细探讨了iOS和Android两大平台的开发技术,包括编程语言、开发工具、框架和最佳实践,帮助开发者深入了解并高效地进行移动应用开发。

进程:系统中正在运行的一个应用程序。(进程之间是相互独立的)

 线程:进程的基本执行单元,一个进程的所有任务都在线程中执行,每个进程都至少有一个线程。

 

 来说说线程的五个状态:

 1、新建:实例化线程对象

 2、就绪:调用start方法。线程对象被装进可调度缓存区中等待CPU调用。

 3、运行:线程执行完成之前,状态会在就绪运行之间来回切换。这个是cpu负责的,我们不能干预。

 4、阻塞:调用sleep方法或者加锁。线程对象变成阻塞状态后,会从可调度线程池中移除,CPU不再调度它。

 5、死亡:程序执行完或者在线程内部调用[NSThread exit]方法或者在线程外调用[线程对象 cancel]

 

 

 先来说一下pThread的使用<这个因为太麻烦因此没人使用>这个是C

 使用的时候需要导入pthread头文件

 - (void)viewDidLoad {

 [super viewDidLoad];

 //声明pthread的对象

 pthread_t threadT;

 NSString *context = @"kaka";


 *  创建pthread

 *  id需要转成void *,在ARC里,需要使用__bridge 进行桥联

 *  @param pthread_t *restrict 需要开启的线程变量

 *  @param  pthread_attr_t   线程的属性

 *  @param  void *(*)(void *) 该线程中执行的方法

 *  @param void *restrict 传入的参数(这里需要注意,id需要转成void *,在ARC里,需要使用__bridge 进行桥联,桥联是临时性的,因此不会交出所有权)  pthread_create(&threadT , NULL, run , (__bridge void *)(context));

}


void *run(void *param){//这里需要注意的时候,它是void * 不是void,因此需要返回NULL

    NSLog(@"var = %@,thread = %@",param,[NSThread currentThread]);

    return NULL;

}


在来说一下NSthread,这个OC的。(这是之前的系统使用,但是现在也基本不使用了)

 先说说线程的创建方式:

 隐式创建

 - (void)viewDidLoad {

 [super viewDidLoad];

 [self performSelectorInBackground:@selector(runOther) withObject:nil];

 }

 

 - (void)runOther{

 NSLog(@"%@",[NSThread currentThread]);

 }

 

 使用thread的方式创建

 1alloc的方式

 - (void)viewDidLoad {

 [super viewDidLoad];

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

 //让线程工作,注意后面我们使用GCD方式的时候也可以使用start,但是它是在主线程上运行的。这里不是。

 [thread start];

 }

 

 - (void)runOther{

 NSLog(@"%@",[NSThread currentThread]);

 }

 

 2detach的方式

 - (void)viewDidLoad {

 [super viewDidLoad];

 //这里当执行该方法后,会帮我们自动启动。不需要调用start启动。

 [NSThread detachNewThreadSelector:@selector(runOther) toTarget:self withObject:nil];

 }

 

 - (void)runOther{

 NSLog(@"%@",[NSThread currentThread]);

 }

 

 

 来了解一下线程的属性

 name:线程的名字

 threadPriority:线程的优先级(取值范围[0.0 - 1.0] 默认0.5 <一般不修改优先级>

            

             isExecuting    线程是否正在执行

 线程的状态值:isFinished     线程是否执行完毕

             isCancelled    线程是否被取消

 

 stackSize: 堆栈大小。除了主线的大小为1M,所有的子线程都是513K

 

 thread里面的方法:

 start:将线程对象加入到可调度线程池中等待CPU调度。

 cancel:取消线程的执行。

 

 

 上代码:

 - (void)viewDidLoad {

     [super viewDidLoad];

     //多线程测试的时候需要多次测试,才能出结果

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

     thread.name = @"2B线程";

     thread.threadPriority = 0.1;

     [thread start];

     NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(runOther) object:nil];

     thread1.name = @"sB线程";

     thread1.threadPriority = 1.0;

     [thread1 start];

 }

 

 - (void)runOther{

     if([[NSThread currentThread].name isEqualToString:@"sB线程"]){

        [[NSThread currentThread] cancel];

     }

     if([NSThread currentThread].isCancelled){

        NSLog(@"%@被取消了",[NSThread currentThread]);

        return ;

     }

     

     if([NSThread currentThread].isExecuting){

        NSLog(@"%@在执行中",[NSThread currentThread]);

     }

     if([NSThread currentThread].isFinished){

        NSLog(@"%@执行完了",[NSThread currentThread]);

     }

        NSLog(@"%@",[NSThread currentThread]);

     }

 

 

 

 

 来说一下线程间通讯,直接上代码:

 

 1performSelectorMainThread方式

 - (void)viewDidLoad {

     [super viewDidLoad];

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

     thread.name = @"我不是主线程";

     [thread start];

 }

 

 -(void)communicationTo{

 

    NSLog(@"看看我%@",[NSThread currentThread]);

    //waitUntilDone这个是代表是否要等待该线程结束之后在运行下面的代码

    [self performSelectorOnMainThread:@selector(comunication) withObject:@"哈哈" waitUntilDone:YES];

    NSLog(@"来试一下啊");

 }

 

 - (void)comunication{

    NSLog(@"%@",[NSThread currentThread]);

 }

 

 

 2performSelector方式

 - (void)viewDidLoad {

     [super viewDidLoad];

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

     thread.name = @"我不是主线程";

     [thread start];

 }

 

 -(void)communicationTo{

     NSLog(@"看看我%@",[NSThread currentThread]);

     //    waitUntilDone这个是代表是否要等待该线程结束之后在运行下面的代码

     [self performSelector:@selector(comunication) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];

     NSLog(@"来试一下啊");

 }

 - (void)comunication{

    NSLog(@"%@",[NSThread currentThread]);

 }

 

 

 

 

 

 在来说说多线程的安全问题,由于线程之间都在抢夺资源。从而造成了线程的问题。直接上代码

 @property(nonatomic,assign)NSInteger ticketNum;

 

 - (void)viewDidLoad {

     [super viewDidLoad];

     self.ticketNum = 20;

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

     thread.name = @"一号";

     [thread start];

     NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicks) object:nil];

     thread1.name = @"二号";

     [thread1 start];

 }

 - (void)saleTicks{

     while (1) {

        //让当前线程睡1

        [NSThread sleepForTimeInterval:1.0];

         if(self.ticketNum == 0){

             NSLog(@"票卖完了....");

             break;

         }else{

             self.ticketNum--;

             NSLog(@"%@剩余票数%ld",[NSThread currentThread],self.ticketNum);

         }

     }

 }

 

 

 解决方法:

 加锁,互斥锁

 加锁,锁定的代码尽量少。

 加锁范围内的代码, 同一时间只允许一个线程执行

 互斥锁的参数:任何继承 NSObject *对象都可以。

 要保证这个锁,所有的线程都能访问到而且是所有线程访问的是同一个锁对象

 - (void)viewDidLoad {

     [super viewDidLoad];

     self.ticketNum = 20;

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

     thread.name = @"一号";

     [thread start];

     NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicks) object:nil];

     thread1.name = @"二号";

     [thread1 start];

 }

 

 - (void)saleTicks{

     @synchronized(self){

         while (1) {

         //让当前线程睡1

         [NSThread sleepForTimeInterval:1.0];

             if(self.ticketNum == 0){

                 NSLog(@"票卖完了....");

                 break;

             }else{

                 self.ticketNum--;

                 NSLog(@"%@剩余票数%ld",[NSThread currentThread],self.ticketNum);

             }

         }

     }

 }

 

 

 

 在来说说NSThread里面的一些类方法

 

 exit:终止当前线程的执行。

 

 isMainThread:判断是否是主线程。

 

 MainThread:返回主线程对象。

 

 isMultiThreaded 判断是否是多线程。

 

 再来说一下互斥锁与自旋锁

 自旋锁其实早在之前我们都已经经常使用,noatomicatomic

 

 

 原子性与非原子性的实质就是一个加了自旋锁。

 来说说原子性与非原子性的区别当然不再是简单的线程安全,原子属性是针对多线程设计的,它的属性实现单线程写多线程读。

 直接上代码模拟:

 @synthesize atomicObj = _atomicObj;

 

 -(void)setAtomicObj:(NSObject *)atomicObj{

 

     @synchronized(self){

     

     _atomicObj = atomicObj;

 

     }

 }

 

 - (NSObject *)atomicObj{

 

    return _atomicObj;

 

 }


 

 在来说一下互斥锁与自旋锁的区别

    共同点:它们都可以锁定一段代码,同一时间,只有一个线程可以执行这段代码。

    区别:

        互斥锁:在锁定的时候,其他线程会进入睡眠状态,只有等待条件满足,再唤醒。

        自旋锁:在锁定的时候,其他线程会做死循环,一直等待条件满足,一旦满足,马上执行。

 

 还有一个注意点:

 所有的UI控件都是不安全的,因此我们在更新UI的时候最好选择主线程,这个也是苹果约定的。

 选择主线程更新UI的好处:

    1、避免出现多个线程同时改变同一个UI控件。

    2、主线程的优先级最高,因此UI的更新优先级也高。

 

 

 

 每一个线程都有一个runLoop,但是只有主线程的runloop是开启的。

 运行循环:(runloop

  它的作用: 1、保存程序不退出。

           2、监听所有时间。

  特征:没有事件的时候就进入休眠状态,一旦监听到事件,就立即响应。

 

 

 只有启动runLoop才会创建自动释放池

 自动释放池的原理:在程序执行过程中,每个事件都会被加上autorelease。当自动释放池被销毁或者耗尽的时候会向池中的所有对象发送release

 

 来我们说以下流程:

    在刚启动程序的时候,如果发生了某一事件,会创建事件对象,然后创建自动释放池,把事件装入自动释放池中。在程序退出的时候,销毁自动释放池,则向池中的每一个对象发送一次release操作。

    

 

 

 

 GCD(这个用得比较普遍)它是C语言的"并发"框架

 它会自动管理线程的生命周期

 只需告诉GCD如何执行什么任务,不需要编写线程管理的代码。

 使用方式:将任务(block)添加到队列(串行/并发(全局)),指定 执行任务的方法(同步(阻塞)/异步)

 

 里面的内容:队列|执行方式|任务

 

 队列:串行|并发

 串行队列:一个任务一个任务执行。

 

 并发队列:在多个任务同时执行。

 

 同步执行:执行完一句代码,在执行后续的代码。但是它是需要马上执行的(之后在介绍主线程的时候会例举出来)。<不会产生新线程>

 

 异步执行:不需要等待某一句代码执行完,就可以执行下一句代码。注意它不是需要立即执行的,它可以等待(之后在介绍主线程的时候会例举出来)。<会产生新线程>

 

 上代码:

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    [self createTest2];

 }

 

 //创建串行异步(特性:会创建一个新的线程,并且所有的事件都在新线程上面按顺序执行)

 -(void)createTest1{

      //创建队列

     //<#dispatch_queue_attr_t attr#>使用NULL(串行),这里是设置队列的类型

     dispatch_queue_t queue = dispatch_queue_create("我是串行异步", NULL);

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

        //创建执行方式

         dispatch_async(queue, ^{

         NSLog(@"%@-----%d",[NSThread currentThread],i);

         });

     }

 }

 

 //创建并行同步(虽然并行可以并发多个任务同时执行,但是由于缺少线程,所以只能像串行同步那样执行了。)

 -(void)createTest2{

     dispatch_queue_t  queue =  dispatch_queue_create("我是并行异步", DISPATCH_QUEUE_CONCURRENT);

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

         dispatch_sync(queue, ^{

         NSLog(@"%@-----%d",[NSThread currentThread],i);

         });

     }

 }

 

 //创建并行异步(会创建多个线程,并且个数是有底层决定的。无法估计。)

 - (void)createTest3{

     dispatch_queue_t  queue = dispatch_queue_create("我是并行异步", DISPATCH_QUEUE_CONCURRENT);

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

         dispatch_async(queue, ^{

         NSLog(@"%@-----%d",[NSThread currentThread],i);

     });

}


 

 来说说全局队列

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

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

         //全局队列与并发队列很相似,但是全局队列没有名称,在MRC中,全局队列不需要释放。然后并发队列在MRC中需要释放。

         //long identifier优先级     unsigned long flags保留的未来参数

         dispatch_async(dispatch_get_global_queue(0, 0), ^{

            NSLog(@"%@",[NSThread currentThread]);

         });

     }

 }

 

 

 在来说说主队列

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

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

         //主队列于串行队列很相似,注意如果主线程上有任务执行,会等待主线程空闲后再调度任务执行

         dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"%@",[NSThread currentThread]);

         });

     }

 }


 在主队列里面添加同步执行的任务是不可以的,会产生死锁。因为同步执行的任务需要马上执行。但是主线程上还没执行完又非得等待同步执行的任务执行完后才能执行,因此造成了相互等待的状况。

 

 在主队列里面添加异步执行的任务是可以的,不会产生死锁。因为异步执行的任务不需要需要马上执行。

 

 

 这么多队列,在开发中我们需要选择使用,下面是给的参考

    串行队列:执行效率低,对执行顺序要求高,性能消耗小。省电

    并发队列:执行效率高,对执行顺序要求不高,性能消耗大。耗电


 GCD里面的延迟执行与只执行一次的方法太简单就不介绍了。

 

 

 调度组:

    使用场景:需要在多个耗时操作执行完毕后,在做统一的处理。

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

     //创建调度组对象

     dispatch_group_t group = dispatch_group_create();

     //创建队列

     dispatch_queue_t queue1 = dispatch_queue_create("我是要放进调度组的队列,并且我是并发的", DISPATCH_QUEUE_CONCURRENT);

     //创建操作

     dispatch_group_async(group, queue1, ^{

        NSLog(@"1%@",[NSThread currentThread]);

     });

     dispatch_group_async(group, queue1, ^{

        NSLog(@"2%@",[NSThread currentThread]);

     });

     

     dispatch_group_async(group, queue1, ^{

        NSLog(@"3%@",[NSThread currentThread]);

     });

     //创建通知,用来做最后的统一处理操作

     dispatch_group_notify(group, dispatch_get_main_queue(), ^{

     NSLog(@"执行完了哈");

     });

 }


 

 

 NSOperation这是苹果最推荐使用的东西,使用程度与GCD差不多。

  相当于GCD来说,使用更加简单。

 

 如何抉择使用GCDNSOperation参考如下:

 CGD中,不好实现最大并发数操作,不好坐依赖关系。

 NSOperation中,无法做到一次性执行,延迟执行。做调度组也较复杂。

 

 NSOperation的使用方式:将NSOperation添加到NSOperationQueue就可以实现。

 但是NSOperation是抽象类,不能直接使用,需要依靠它的两个子类。

 来说说它的两个子类

 NSInvocationOperation(这个使用得很少)

 

 NSBlockOperation(这个是最常使用的)

 

 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{

 NSLog(@"1. 下载一个小说的压缩包, %@",[NSThread currentThread]);

 }];

 NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{

 NSLog(@"2. 解压缩,删除压缩包, %@",[NSThread currentThread]);

 }];

 NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{

 NSLog(@"3. 更新UI, %@",[NSThread currentThread]);

 }];

 

 指定任务之间的依赖关系 -- 依赖关系可以跨队列(可以在子线程下载完,到主线程更新UI)

 [op2 addDependency:op1];

 [op3 addDependency:op2];

 注意点:一定不要出现循环依赖关系

  例如加上这句  [op1 addDependency:op3];

 

 // waitUntilFinished 类似GCD的调度组的通知

 // NO 不等待,会直接执行  NSLog(@"come here");

 // YES 等待上面的操作执行结束,再 执行  NSLog(@"come here")

 [self.opQueue addOperations:@[op1, op2] waitUntilFinished:YES];

 

 // 取消队列的所有操作

 [self.opQueue cancelAllOperations]; // 取消队列的所有操作,会把任务从队列里面全部删除

 

 NSLog(@"取消所有的操作");

 

 // 取消队列的挂起状态

 // (只要是取消了队列的操作,我们就把队列处于启动状态。以便于队列的继续)

 self.opQueue.suspended = NO;


 

 // 设置最大的并发数是2 (最大并发数,不是线程的数量。 而是同时执行的操作的数量)

 self.opQueue.maxConcurrentOperationCount = 2;


 

 // 判断操作的数量,当前队列里面是不是有操作

 if (self.opQueue.operationCount == 0) {

    NSLog(@"没有操作");

    return;

}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值