IOS开发之 -- 线程初识

对于开发者来说,多线程永远有这一层神秘的色彩,似乎是一到迈步过去的坎,在同步、异步、串行、并行、死锁这几个名字当中,逐渐的放弃治疗,下面就多线程,谈一下自己的认识,理解的肯定不全面,只是一些简单的皮毛,特在此记录下,哪位大神看到了,还希望能多多指正!

首先:什么是线程,线程和进程的区别和联系

  • 一个程序至少要有进城,一个进程至少要有一个线程.

  • 进程:资源分配的最小独立单元,进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

  • 线程:进程下的一个分支,是进程的实体,是CPU调度和分派的基本单元,它是比进程更小的能独立运行的基本单位,线程自己基本不拥有系统资源,只拥有一点在运行中必不可少的资源(程序计数器、一组寄存器、栈),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源。

  • 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。

  • 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些

什么是多线程编程?

  • NSThread:当需要进行一些耗时操作时会把耗时的操作放到线程中。线程同步:多个线程同时访问一个数据会出问题,NSlock、线程同步块、@synchronized(self){}。

  • NSOperationQueue操作队列(不需考虑线程同步问题)。编程的重点都放在main里面,NSInvocationOperationBSBlockOperation、自定义Operation。创建一个操作绑定相应的方法,当把操作添加到操作队列中时,操作绑定的方法就会自动执行了,当把操作添加到操作队列中时,默认会调用main方法。

  • GCD(`Grand Central Dispatch)宏大的中央调度,串行队列、并发队列、主线程队列;

  • 同步和异步:同步指第一个任务不执行完,不会开始第二个,异步是不管第一个有没有执行完,都开始第二个。

  • 串行和并行:串行是多个任务按一定顺序执行,并行是多个任务同时执行;

  • 代码是在分线程执行,在主线程嘟列中刷新UI。

多线程编程是防止主线程堵塞、增加运行效率的最佳方法。

  • Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法;

  • 一个NSOperationQueue操作队列,相当于一个线程管理器,而非一个线程,因为你可以设置这个线程管理器内可以并行运行的线程数量等。

  • 多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径。

  • iPhoneOS下的主线程的堆栈大小是1M。第二个线程开始就是512KB,并且该值不能通过编译器开关或线程API函数来更改,只有主线程有直接修改UI的能力。

定时器与线程的区别;

  • 定时器;可以执行多次,默认在主线程中。

  • 线程:只能执行一次。

GCD初始:

一条重要的准则

一般来说,我们使用GCD的最大目的是在新的线程中同时执行多个任务,这意味着我们需要两项条件:

 

  • 能开启新的线程

  • 任务可以同时执行

  • 结合以上两个条件,也就等价“开启新线程的能力 + 任务同步执行的权利”,只有在满足能力与权利这两个条件的前提下,我们才可以在同时执行多个任务。

 所有组合的特点:

各个组合下的代码执行:

//异步执行+并行队列
-(void)asynConcurrent
{
    //并行队列
    dispatch_queue_t two_queue = dispatch_queue_create("hero.hel.11223", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"---start---");
    
    dispatch_async(two_queue, ^{
        NSLog(@"任务一---%@",[NSThread currentThread]);
    });
    
    dispatch_async(two_queue, ^{
        NSLog(@"任务二---%@",[NSThread currentThread]);
    });
    
    dispatch_async(two_queue, ^{
        NSLog(@"任务三---%@",[NSThread currentThread]);
    });
    
    NSLog(@"--end---");
}

打印结果:

2017-04-06 14:42:34.500 线程的练习[2093:152260] ---start---
2017-04-06 14:42:34.501 线程的练习[2093:152260] --end---
2017-04-06 14:42:34.501 线程的练习[2093:152296] 任务一---<NSThread: 0x618000078480>{number = 3, name = (null)}
2017-04-06 14:42:34.501 线程的练习[2093:152299] 任务二---<NSThread: 0x60000007d100>{number = 4, name = (null)}
2017-04-06 14:42:34.501 线程的练习[2093:152297] 任务三---<NSThread: 0x6180000728c0>{number = 5, name = (null)}

异步执行意味着:
1、可以开启新的线程
2、任务可以先绕过不执行,回头再来执行

并行队列意味着:
1、任务之间不需要排队,且具有同时被执行的“权利”
两者组合后的结果:
1、开了三个新线程
2、函数在执行时,先打印了start和end,再回头执行这三个任务
3、这三个任务是同时执行,没有先后


 1 //同步执行+串行队列
 2 -(void)synSerial
 3 {
 4     //串行队列
 5     dispatch_queue_t one_queue = dispatch_queue_create("hero.hgl.112233", DISPATCH_QUEUE_SERIAL);
 6     
 7     NSLog(@"--开始--");
 8     
 9     dispatch_sync(one_queue, ^{
10         NSLog(@"任务1---%@",[NSThread currentThread]);
11     });
12     
13     dispatch_sync(one_queue, ^{
14         NSLog(@"任务2---%@",[NSThread currentThread]);
15     });
16     
17     dispatch_sync(one_queue, ^{
18         NSLog(@"任务3---%@",[NSThread currentThread]);
19     });
20     
21     NSLog(@"--结束--");
22 }

打印结果:

2017-04-06 14:42:34.502 线程的练习[2093:152260] --开始--
2017-04-06 14:42:34.505 线程的练习[2093:152260] 任务1---<NSThread: 0x618000075340>{number = 1, name = main}
2017-04-06 14:42:34.505 线程的练习[2093:152260] 任务2---<NSThread: 0x618000075340>{number = 1, name = main}
2017-04-06 14:42:34.506 线程的练习[2093:152260] 任务3---<NSThread: 0x618000075340>{number = 1, name = main}
2017-04-06 14:42:34.507 线程的练习[2093:152260] --结束--

这里的执行原理和步骤图跟“同步执行+并发队列”是一样的,只要是同步执行就没法开启新的线程,所以多个任务之间也一样只能按顺序来执行

//异步执行+串行队列
-(void)asynSerial
{
    //串行队列
    dispatch_queue_t h_queue = dispatch_queue_create("hero112233.com", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"开始了----");
    
    dispatch_async(h_queue, ^{
        NSLog(@"a计划 -- %@", [NSThread currentThread]);
    });
    
    dispatch_async(h_queue, ^{
        NSLog(@"b计划 -- %@", [NSThread currentThread]);
    });
    
    dispatch_async(h_queue, ^{
        NSLog(@"c计划 -- %@", [NSThread currentThread]);
    });
    
    NSLog(@"--结束了---");
}

打印结果:

2017-04-06 15:35:37.916 线程的练习[2376:173399] 开始了----
2017-04-06 15:35:37.916 线程的练习[2376:173399] --结束了---
2017-04-06 15:35:37.916 线程的练习[2376:173459] a计划 -- <NSThread: 0x600000075d00>{number = 3, name = (null)}
2017-04-06 15:35:37.917 线程的练习[2376:173459] b计划 -- <NSThread: 0x600000075d00>{number = 3, name = (null)}
2017-04-06 15:35:37.917 线程的练习[2376:173459] c计划 -- <NSThread: 0x600000075d00>{number = 3, name = (null)}

异步执行意味着:
1、可以开启新的线程
2、任务可以先绕过不执行,回头再来执行
串行队列意味着:
1、任务必须按添加进队列的顺序挨个执行
两者组合后的结果:
1、开启了一个新的子线程
2、函数在执行时,先打印了开始和结束,再回头执行这三个任务
3、三个任务是按顺序执行的,所以打印结果是“任务a-->任务b-->任务c”

//同步执行+并行队列
-(void)synConcurrent
{
    //并行队列
    dispatch_queue_t r_queue = dispatch_queue_create("aaaa.com", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"@@@开始了");
    
    dispatch_sync(r_queue, ^{
        NSLog(@"a1计划 -- %@", [NSThread currentThread]);
    });
    
    dispatch_sync(r_queue, ^{
        NSLog(@"a2计划 -- %@", [NSThread currentThread]);
    });
    
    dispatch_sync(r_queue, ^{
        NSLog(@"a3计划 -- %@", [NSThread currentThread]);
    });
    
    NSLog(@"@@@@结束了");
}

打印结果:

2017-04-06 15:40:24.100 线程的练习[2410:175780] @@@开始了
2017-04-06 15:40:24.135 线程的练习[2410:175780] a1计划 -- <NSThread: 0x6000000734c0>{number = 1, name = main}
2017-04-06 15:40:24.136 线程的练习[2410:175780] a2计划 -- <NSThread: 0x6000000734c0>{number = 1, name = main}
2017-04-06 15:40:24.136 线程的练习[2410:175780] a3计划 -- <NSThread: 0x6000000734c0>{number = 1, name = main}
2017-04-06 15:40:24.136 线程的练习[2410:175780] @@@@结束了

同步执行意味着:
1、不能开启新的线程
2、任务创建后必须执行完才能往下走
并行队列意味着:
1、任务必须按添加进队列的顺序挨个执行
两者结合:
1、所有任务都只能在主线程中执行
2、函数在执行时,必须按照代码的书写顺序一行一行的执行完才能继续
注意:
在这里即便是并行队列,任务可以同时执行,但是由于只存在一个主线程,所以没法把任务分发到不同的线程去同步处理,期结果就是只能在主线程里
按顺序挨个执行了
//异步执行+主队列
-(void)asynmainQueue
{
    //主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    NSLog(@"@@@start");
    
    dispatch_async(mainQueue, ^{
        NSLog(@"a11计划 -- %@", [NSThread currentThread]);
    });
    
    dispatch_async(mainQueue, ^{
        NSLog(@"a22计划 -- %@", [NSThread currentThread]);
    });
    
    dispatch_async(mainQueue, ^{
        NSLog(@"a33计划 -- %@", [NSThread currentThread]);
    });
    
    NSLog(@"@@@end");
}

打印结果:

2017-04-06 14:42:34.532 线程的练习[2093:152260] @@@start
2017-04-06 14:42:34.532 线程的练习[2093:152260] @@@end
2017-04-06 14:42:34.708 线程的练习[2093:152260] a11计划 -- <NSThread: 0x618000075340>{number = 1, name = main}
2017-04-06 14:42:34.709 线程的练习[2093:152260] a22计划 -- <NSThread: 0x618000075340>{number = 1, name = main}
2017-04-06 14:42:34.709 线程的练习[2093:152260] a33计划 -- <NSThread: 0x618000075340>{number = 1, name = main}

异步执行意味着:
1、可以开启新的线程
2、任务可以先绕过去不执行,回头再来执行
主队列和串行队列的区别:
1、队列中的任务一样要按顺序执行
2、主队列中的任务必须在主线程中执行,不允许在子线程中执行

//同步执行+主队列 -->死锁
-(void)synmainQueue
{
    //主队列
    dispatch_queue_t hmainQueue = dispatch_get_main_queue();
    
    NSLog(@"@@@start");

    dispatch_sync(hmainQueue, ^{
        NSLog(@"a11计划 -- %@", [NSThread currentThread]);
    });
    
    dispatch_sync(hmainQueue, ^{
        NSLog(@"a22计划 -- %@", [NSThread currentThread]);
    });
    
    dispatch_sync(hmainQueue, ^{
        NSLog(@"a33计划 -- %@", [NSThread currentThread]);
    });
    

    NSLog(@"@@@end");
}

打印结果:

2017-04-06 15:57:06.447 线程的练习[2485:181839] @@@start

解释:
1、主队列中的任务必须按顺序挨个执行
2、任务1要等主线程有空的时候(即主队列中的所有任务执行完)才能执行
3、主线程要执行完“end”的任务才有空
4、a11计划和end两个任务互相等等,造成死锁

原文地址:https://www.cnblogs.com/hero11223/p/6674103.html