iOS-多線程介紹

1、前言部分

 最近在面試,從新溫習了一遍多線程,但願加深一遍對於多線程的理解。程序員

一、什麼是進程?

1).要了解線程咱們必須先了解進程,通俗來說進程就是在系統中運行的一個應用程序。面試

2).每一個線程之間是獨立存在的,分別運行在其專用的且受保護的內存空間中。安全

3).好比打開QQ或Xcode系統會分別開啓兩個進程 如圖:多線程

4)、咱們能夠經過"活動監視器"查看Mac系統中所開啓的進程。併發

二、什麼是線程?

1).一個進程要想執行任務必須得有線程,即一個進程至少要有一個線程。異步

2).線程是進程的基本執行單元,一個進程(程序)的全部任務都是在線程中執行的。async

3).好比使用酷狗播放音樂、使用迅雷下載電影都須要在線程中運行 如圖:ide

三、什麼是線程的串行?

1).一個線程中任務是串行執行的(順序執行)的,也就是說一個線程同一時間內只能執行一個任務。函數

2).串行執行圖解,好比一個線程下載3個文件(文件A、B、C) 性能

四、什麼是多線程?

1).一個進程中能夠開啓多個線程,每一個線程能夠併發(同時)執行不一樣的任務。

2).相似關係列舉:進程---->車間;線程---->車間工人

3).多線程圖解,好比同時開啓3個線程分別下載3個文件(文件A、B、C

 

五、多線程原理

1).同一時間CPU只能執行一個線程,只有一個線程在工做(執行)。

2).多線程併發(同時)執行,實際上是CPU快速的在多個線程之間調度(切換)。

3).若是CPU調度線程的速度夠快,就會形成多線程併發執行的假象。

4).多線程的缺點:

   一、每一個線程都會佔用必定的內存空間(默認狀況下:主線程佔用1MB,子線程佔用512KB),

        若是開啓線程過多會佔用大量的內存空間於是形成程序性能下降。

   二、線程越多CPU調度線程上的開銷就越大(相似工廠工人越多,工廠開銷也越大)。

   三、使程序設計更復雜:好比多線程的數據通訊,多線程之間的數據共享。

5).多線程的優勢:

   一、能適當提升程序的執行效率。

   二、能適當提升資源利用率(CPU、內存的利用率)

六、什麼是主線程?

 1).一個iOS程序開啓後默認會開啓一個線程,這個線程被稱爲"主線程"或"UI線程"。

 2).主線程的主要做用:

    一、顯示/刷新UI界面

    二、處理UI事件(好比點擊事件、滾動事件、拖拽事件等)

 3).主線程注意點:

    一、別將耗時的操做放在主線程中,耗時操做放在主線程中會形成程序卡頓的問題。

七、耗時操做Demo演示   

1)、直接在主線程中運行的Demo

 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{  2     //獲取當前執行方法和當前線程  3     //number==1 主線程  4     //number!=1 其餘線程、子線程、次線程
 5     NSLog(@"%s----%@",__func__,[NSThread currentThread]);  6     
 7     //直接在主線程中運行 形成UI操做卡頓
 8  [self longTimeOperation];  9 } 10 
11 #pragma mark-耗時操做
12 -(void)longTimeOperation{ 13     for (int i=0; i<20000; i++) { 14         NSLog(@"%d",i); 15  } 16 }
View Code

2)、在子線程中運行的Demo

 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{  2     //獲取當前執行方法和當前線程  3     //number==1 主線程  4     //number!=1 其餘線程、子線程、次線程
 5     NSLog(@"%s----%@",__func__,[NSThread currentThread]);  6     
 7     //將耗時操做放在子線程中執行,不影響UI的操做
 8  [self performSelectorInBackground:@selector(longTimeOperation) withObject:nil];  9 } 10 
11 #pragma mark-耗時操做
12 -(void)longTimeOperation{ 13     for (int i=0; i<20000; i++) { 14         NSLog(@"%d",i); 15  } 16 }
View Code

八、iOS中多線程的實現方案

技術方案 簡介 語言 線程生命週期 使用頻率
pthread
  • 一套通用的多線程API
  • 適用於Unix\Linux\Windows等系統
  • 跨平臺可移植
  • 使用難度大
C 程序員管理 幾乎不用
NSThread
  • 使用更加面向對象
  • 簡單易用直接操做線程對象
OC 程序員管理 偶爾使用
GCD
  • 旨在替換NSThread等線程技術
  • 充分利用設備的多核
C 自動管理 常用
NSOperation
  • 基於GCD(底層是GCD)
  • 比GCD多了一些更簡單實用的功能
  • 使用更加面向對象
OC 自動管理 常用

九、pthread 的使用 

 1 #import <pthread.h>
 2 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
 3     [self test];
 4 }
 5 
 6 //使用pthread建立線程
 7 -(void)test{
 8     //聲明一個線程變量
 9     pthread_t threadID;
10     
11     /*
12       參數:
13        一、要開的線程的變量
14        二、線程的屬性
15        三、要在這個子線程中執行的函數(任務)
16        四、這個函數(任務)須要傳遞的參數
17      */
18     //pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict);
19     
20     id str=@"hello";
21     //id 須要轉成void * 須要使用__bridge進行橋連
22     //一、這裏只是臨時把str對象轉成void *在這裏臨時使用,不改變這個對象的全部權
23     //二、把對象全部權交出去,在這個函數把str轉成void *
24     //若是使用MRC 這裏不須要使用橋連能夠直接使用這個參數
25     //ARC自動內存管理,本質是編譯器特性,是在程序編譯的時候,編譯器在適合的地方幫咱們添加retain、release、autorelease
26     pthread_create(&threadID, NULL, run, (__bridge void*)str);
27 }
28 
29 #pragma mark-耗時操做
30 void *run(void *prama){
31      //void * 至關於OC裏面的id
32     //__bridge 橋連操做
33     NSString *str=(__bridge NSString*)prama;
34     for (int i=0; i<20000; i++) {
35         NSLog(@"%d----%@----%@",i,[NSThread currentThread],str);
36 
37     }
38     return NULL;
39 }
View Code

十、NSThread 的建立方式 

 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
 2     //[self test1];
 3     
 4     //[self test2];
 5     
 6     [self test3];
 7 }
 8 
 9 #pragma mark-線程的建立方式
10 //建立線程方式1
11 -(void)test1{
12     //實例化一個線程對象
13     NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(run1) object:nil];
14     //線程啓動方法
15     [thread start];
16 }
17 
18 //建立線程方式2
19 -(void)test2{
20     //建立線程後自動啓動線程
21     [NSThread detachNewThreadSelector:@selector(run2:) toTarget:self withObject:@"KK"];
22 }
23 
24 //建立線程方式3
25 -(void)test3{
26     //隱式建立線程並啓動
27     [self performSelectorInBackground:@selector(run2:) withObject:@"Hi"];
28     
29     /*
30      test二、test3 兩個線程建立方法的優缺點:
31      優勢:簡單快捷
32      缺點:沒法對線程更詳細的設置
33      */
34 }
35 
36 #pragma mark-耗時操做
37 -(void) run1{
38     for (int i=0; i<200; i++) {
39         NSLog(@"%d----%@",i,[NSThread currentThread]);
40     }
41 }
42 -(void) run2:(NSString*)param{
43     for (int i=0; i<200; i++) {
44         NSLog(@"%d----%@---%@",i,[NSThread currentThread],param);
45     }
46 }
View Code

 十一、線程的屬性介紹

 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
 2     [self test];
 3 }
 4 
 5 #pragma mark-線程的屬性
 6 -(void)test{
 7     //實例化一個線程對象
 8     NSThread *threadA=[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
 9     //線程名稱,用於區分多個線程
10     threadA.name=@"Thread A";
11     //線程的優先級 0.0~1.0 默認值 0.5
12     //實際開發中通常不修改優先級的值
13     threadA.threadPriority=0.1;
14     //線程啓動方法
15     [threadA start];
16     
17 //    //實例化一個線程對象
18 //    NSThread *threadB=[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
19 //    //線程名稱,用於區分多個線程
20 //    threadB.name=@"Thread B";
21 //    //線程的優先級 0.0~1.0 默認值 0.5
22 //    threadB.threadPriority=1.0;
23 //    //線程啓動方法
24 //    [threadB start];
25 }
26  
27 
28 #pragma mark-耗時操做
29 -(void) run{
30     for (int i=0; i<200; i++) {
31         NSLog(@"%d----%@",i,[NSThread currentThread]);
32     }
33 }
View Code

十二、GCD的使用實例

 1 #pragma mark - GCD演練
 2 /**
 3  併發隊列,同步執行
 4  */
 5 - (void)gcdDemo4 {
 6     // 1. 隊列
 7     dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
 8     
 9     // 2. 同步執行任務
10     for (int i = 0; i < 10; i++) {
11         dispatch_sync(queue, ^{
12             NSLog(@"%@ %d", [NSThread currentThread], i);
13         });
14     }
15 }
16 
17 /**
18  併發隊列,異步執行
19  */
20 - (void)gcdDemo3 {
21     // 1. 隊列
22     dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
23     
24     // 2. 異步執行任務
25     for (int i = 0; i < 10; i++) {
26         dispatch_async(queue, ^{
27             NSLog(@"%@ %d", [NSThread currentThread], i);
28         });
29     }
30     
31 }
32 
33 /**
34  串行隊列,異步執行
35  */
36 - (void)gcdDemo2 {
37     // 1. 隊列
38     dispatch_queue_t queue = dispatch_queue_create("itcast", NULL);
39     
40     // 2. 異步執行任務
41     for (int i = 0; i < 10; i++) {
42         dispatch_async(queue, ^{
43             NSLog(@"%@ %d", [NSThread currentThread], i);
44         });
45     }
46 }
47 
48 /**
49  串行隊列,同步執行(開發中很是少用)
50  */
51 - (void)gcdDemo1 {
52 
53     // 1. 隊列
54 //    dispatch_queue_t queue = dispatch_queue_create("icast", DISPATCH_QUEUE_SERIAL);
55     dispatch_queue_t queue = dispatch_queue_create("icast", NULL);
56     NSLog(@"執行前----");
57     
58     // 執行任務
59     for (int i = 0; i < 10; i++) {
60         NSLog(@"調度----");
61         
62         // 在隊列中"同步"執行任務,串行對列添加同步執行任務,會當即被執行
63         dispatch_sync(queue, ^{
64             NSLog(@"%@ %d", [NSThread currentThread], i);
65         });
66     }
67     NSLog(@"for 後面");
68 }

View Code

1三、NSOperation的使用實例 

 1 #pragma mark - 基本演練
 2 // MARK: - 線程間通信
 3 - (void)opDemo5 {
 4     NSOperationQueue *q = [[NSOperationQueue alloc] init];
 5     [q addOperationWithBlock:^{
 6         NSLog(@"耗時操做 %@", [NSThread currentThread]);
 7         
 8         [[NSOperationQueue mainQueue] addOperationWithBlock:^{
 9             NSLog(@"更新UI %@", [NSThread currentThread]);
10         }];
11     }];
12 }
13 
14 // MARK: - 更簡單的
15 - (void)opDemo4 {
16     NSOperationQueue *q = [[NSOperationQueue alloc] init];
17     
18     for (int i = 0; i < 10; i++) {
19         [q addOperationWithBlock:^{
20             NSLog(@"down %@ %@", [NSThread currentThread], @(i));
21         }];
22     }
23 }
24 
25 // MARK: - NSBlockOperation
26 - (void)opDemo3 {
27     NSOperationQueue *q = [[NSOperationQueue alloc] init];
28     
29     for (int i = 0; i < 10; i++) {
30         NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
31             NSLog(@"down %@ %@", [NSThread currentThread], @(i));
32         }];
33         
34         [q addOperation:op];
35     }
36 }
37 
38 // MARK: - NSInvocationOperation
39 - (void)opDemo2 {
40     NSOperationQueue *q = [[NSOperationQueue alloc] init];
41     
42     for (int i = 0; i < 10; i++) {
43         NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@(i)];
44         
45         [q addOperation:op];
46     }
47 }
48 
49 - (void)opDemo1 {
50     NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"Invocation"];
51     
52     // start 會當即在當前線程執行 selector 方法
53     //    [op start];
54     
55     // 將操做添加到隊列,會自動異步執行
56     NSOperationQueue *q = [[NSOperationQueue alloc] init];
57     [q addOperation:op];
58 }
59 
60 - (void)downloadImage:(id)obj {
61     NSLog(@"%@ %@",  [NSThread currentThread], obj);
62 }
View Code 

1四、線程的狀態介紹


 1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
 2     [self test];
 3 }
 4 
 5 #pragma mark-線程的屬性
 6 -(void)test{
 7     //實例化一個線程對象
 8     NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
 9     //放到可調度線程池,等待被調度 這時候是準備就緒狀態
10     [thread start];
11 }
12  
13 
14 #pragma mark-耗時操做
15 -(void) run{
16     
17     NSLog(@"%s",__func__);
18     //線程進來就睡眠2秒
19     //[NSThread sleepForTimeInterval:2.0];
20     
21     //睡到指定的時間點
22     [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
23     
24     for (int i=0; i<200; i++) {
25         //知足一個條件後,阻塞線程的執行
26         if (i==10) {
27             [NSThread sleepForTimeInterval:2.0];
28         }
29         
30         //一旦達到某個條件,就強制終止線程的執行
31         if (i==100) {
32             //一旦終止就不能從新啓動,後面的代碼就不會被執行
33             [NSThread exit];
34         }
35         NSLog(@"%d----%@",i,[NSThread currentThread]);
36     }
37 }
View Code

1五、多線程的安全隱患

1).一塊資源可能會被多個線程共享,也就是多個線程可能訪問同一塊資源。

2).好比多個線程訪問同一個對象、同一個變量、同一個文件。

3).當多個線程訪問同一個資源時,很容易引起數據錯亂和數據安全問題。

1六、解決多線程安全問題

1).添加互斥鎖解決多線程訪問同一資源形成的數據安全問題。

2).互斥鎖使用格式:@synchronized (self) {//須要鎖定的代碼}

3).互斥鎖的優缺點:

  優勢:能有效防止因多線程搶奪同一資源形成的數據安全問題。

  缺點:須要消耗大量的CPU資源(所以蘋果不推薦使用互斥鎖)

4).互斥鎖的使用前提:多個線程搶奪同一資源。

1七、利用屬性的原子性保證數據的安全性

1).atomic 原子屬性--默認屬性

2).原子屬性內部使用的是 自旋鎖

3).自旋鎖與互斥鎖的異同

  共同點:均可以鎖定一段代碼,同一時間內,只有一個線程能執行這段鎖定的代碼。

  不一樣點:互斥鎖 在鎖定的時候其餘線程在等待的時候會進入休眠 等待條件知足時須要從新喚醒。

  自旋鎖在鎖定的時候,其餘線程等待的時候作死循環一直等待條件知足,一旦條件知足就立馬執行,

  少了一個喚醒的過程。

相關文章
相關標籤/搜索