最近在面試,從新溫習了一遍多線程,但願加深一遍對於多線程的理解。程序員
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).主線程注意點:
一、別將耗時的操做放在主線程中,耗時操做放在主線程中會形成程序卡頓的問題。
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 }
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 }
技術方案 | 簡介 | 語言 | 線程生命週期 | 使用頻率 |
pthread |
|
C | 程序員管理 | 幾乎不用 |
NSThread |
|
OC | 程序員管理 | 偶爾使用 |
GCD |
|
C | 自動管理 | 常用 |
NSOperation |
|
OC | 自動管理 | 常用 |
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 }
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 }
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 }
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 #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 }
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 }
1).一塊資源可能會被多個線程共享,也就是多個線程可能訪問同一塊資源。
2).好比多個線程訪問同一個對象、同一個變量、同一個文件。
3).當多個線程訪問同一個資源時,很容易引起數據錯亂和數據安全問題。
1).添加互斥鎖解決多線程訪問同一資源形成的數據安全問題。
2).互斥鎖使用格式:@synchronized (self) {//須要鎖定的代碼}
3).互斥鎖的優缺點:
優勢:能有效防止因多線程搶奪同一資源形成的數據安全問題。
缺點:須要消耗大量的CPU資源(所以蘋果不推薦使用互斥鎖)
4).互斥鎖的使用前提:多個線程搶奪同一資源。
1).atomic 原子屬性--默認屬性
2).原子屬性內部使用的是 自旋鎖
3).自旋鎖與互斥鎖的異同
共同點:均可以鎖定一段代碼,同一時間內,只有一個線程能執行這段鎖定的代碼。
不一樣點:互斥鎖 在鎖定的時候其餘線程在等待的時候會進入休眠 等待條件知足時須要從新喚醒。
自旋鎖在鎖定的時候,其餘線程等待的時候作死循環一直等待條件知足,一旦條件知足就立馬執行,
少了一個喚醒的過程。