在IOS中為什麼使用多線程及多線程實現的三種方法_IOS

來源:互聯網
上載者:User

多線程是一個比較輕量級的方法來實現單個應用程式內多個代碼執行路徑。

在系統層級內,程式並排執行,程式分配到每個程式的執行時間是基於該程式的所需時間和其他程式的所需時間來決定的。

然而,在每個程式內部,存在一個或者多個執行線程,它同時或在一個幾乎同時發生的方式裡執行不同的任務。

概要提示:

iPhone中的線程應用並不是無節制的,官方給出的資料顯示,iPhone OS下的主線程的堆棧大小是1M,第二個線程開始就是512KB,並且該值不能通過編譯器開關或線程API函數來更改,只有主線程有直接修改UI的能力

一、線程概述

有些程式是一條直線,起點到終點——如簡單的hello world,運行列印完,它的生命週期便結束了,像是曇花一現。

有些程式是一個圓,不斷迴圈直到將它切斷——如作業系統,一直運行直到你關機。

一個運行著的程式就是一個進程或者叫做一個任務,一個進程至少包含一個線程,線程就是程式的執行流。

Mac和IOS中的程式啟動,建立好一個進程的同時,一個線程便開始運作,這個線程叫做主線程。主線成在程式中的位置和其他線程不同,它是其他線程最終的父線程,且所有的介面的顯示操作即AppKit或UIKit的操作必須在主線程進行。

系統中每一個進程都有自己獨立的虛擬記憶體空間,而同一個進程中的多個線程則公用進程的記憶體空間。

每建立一個新的進成,都需要一些記憶體(如每個線程有自己的stack空間)和消耗一定的CPU時間。

當多個進成對同一個資源出現爭奪的時候需要注意安全執行緒問題

建立線程

建立一個新的線程就是給進程增加一個執行流,所以建立一個線程需要提供一個函數或者方法作為線程的進口。

1.使用NSThread

NSThread提供了建立線程的路徑,還可以提供了監測當前線程是否是主線程的方法使用NSThread建立一個新的線程有兩種方式:

1.建立一個NSThread的對象,調用Start方法——使用一個目標對象的方法初始化一個NSThread對象,或者建立一個繼承自NSThread的子類,實現起main方法?,然後在直接建立這個子類的對象。

2.使用detachNewThreadSelector:toTarget:withObject:這個類方法建立一個子線程,這個比較直接,直接使用目標對象的方法作為線程啟動入口

2.使用NSObject

使用NSObject直接就加入了對多線程的支援,允許對象的某個方法在後台運行。

[my0bj performSelectorInBackground:@selector(doSomething) withObject:nil];

3.POSIX Thread

由於Mac和IOS都是基於Darwin系統,Darwin系統的UNX核心,是基於mach和BSD的,繼承了BSD的POSIX介面,所以可以直接使用POSIX線程的相關介面開實現線程
建立線程的介面為 pthread_create, 當然在建立線程之前可以建立好相關線程的屬性

——————————————————————————————————————

NSOperation&NSOperationQueue

很多時候我們使用多線程,需要控制線程的並發數,畢竟線程也是需要消耗系統資源的,當程式中同時啟動並執行線程過多時,系統必然變慢,所以很多時候我們會控制同時運行線程的數目
NSOperation可以封裝我們的操作,然後將建立好的NSOperation對象放到NSOperationQueue隊列中,OperationQueue便開始啟動新的線程去執行隊列中的操作,OperationQueue的並發數時可以通過如下方式進行設定的:

- (void)setMaxConcurrentOperationCount:(NSInteger)count

GCD時Grand central Dispatch的縮寫,是一系列BSD層面的介面。在mac10.6和IOS4.0以後才引入的且現在NSOperation和NSOperationQueue的多線程的實現就是基於GCD的。目前這個特性也被移植到 FreeBSD上了,可以查看libdispatch這個開源項目。

dispatch_queue_t imageDownloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

當然,GCD除了處理多線程外還有很多非常好的功能,其建立在強大的kqueue之上,效率也能夠得到保障。

前言

在多線程簡介中,我已經說明過了,為了提高介面的流暢度以及使用者體驗。我們務必要把耗時的操作放到別的線程中去執行,千萬不要阻塞主線程。

下面小編給大家帶來三種ios多線程編程方法:

NSThread
Grand Centeral Dispatch(GCD)
NSOperation和NSOperationQueue

1.NSThread

這是最輕量級的多線程的方法,使用起來最直觀的多線程編程方法。但是因為需要自己管理線程的生命週期,線程同步。經常使用NSThread進行調試,在實際項目中不推薦使用。

//擷取當前線程NSThread *current = [NSThread currentThread];//擷取主線程NSThread *main = [NSThread mainThread];NSLog(@"當前線程 --- %@",current);NSLog(@"主線程 --- %@",main);

控制台輸出結果:

2015-11-22 22:30:29.572 多線程demo[1289:2925847] 當前線程 --- <NSThread: 0x7fc0e1401dc0>{number = 1, name = main}
2015-11-22 22:30:29.572 多線程demo[1289:2925847] 主線程 --- <NSThread: 0x7fc0e1401dc0>{number = 1, name = main}

從結果我們看出當前的線程就是主線程, number 相當於線程的id, name 是線程的名稱,主線程的number就是1

阻塞線程:

//阻塞線程3秒[NSThread sleepForTimeInterval:3];[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];

2.GCD(Grand Central Dispatch)

GCD是基於C語言底層API實現的一套多線程並發機制,非常的靈活方便,在實際的開發中使用很廣泛。

簡單來說CGD就是把 操作 放在 隊列 中去執行。

你只需定義好操作和隊列就可以了,不需要直接控制線程的建立和銷毀,線程的生命週期由隊列來管理。

隊列:負責操作的調度和執行,有先進先出(FIFO)的特點。也就是說先排入佇列的操作先執行,後加入的後執行。

隊列有兩種:

串列隊列:

隊列中的操作只會按順序執行,你可以想象成單視窗排隊。

並行隊列:

隊列中的操作可能會並發執行,這取決與操作的類型,你可以想象成多視窗排隊。

//建立串列隊列dispatch_queue_t q = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);//建立並行隊列dispatch_queue_t q = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);

my_serial_queue和my_concurrent_queue是隊列的名字標籤,為了與其他的隊列區分,在一個項目裡面必須是唯一的。

DISPATCH_QUEUE_SERIAL表示串列隊列

DISPATCH_QUEUE_CONCURRENT表示並行隊列

操作同樣也分兩種類型:

同步操作:只會按順序執行,執行順序是確定的。

非同步作業:在串列隊列中執行順序確定,在並行隊列中執行順序不確定

使用block來定義操作要執行的代碼,q是已經定義好的,操作要加入的隊列

//定義同步操作dispatch_sync(q, ^{ //要執行的代碼 });//定義非同步作業dispatch_async(q, ^{ //要執行的代碼  });

下面我們看一下同步,非同步作業加入到串列和並行隊列裡面,執行的順序和特點:1.同步操作不管加入到何種隊列,只會在主線程按順序執行

dispatch_queue_t q_serial = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);dispatch_queue_t q_concurrent = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);for (int i = 0; i < 5; ++i) { dispatch_sync(q_serial, ^{ NSLog(@"串列隊列裡的同步任務 %@ %d", [NSThread currentThread], i); });}for (int i = 0; i < 5; ++i) { dispatch_sync(q_concurrent, ^{ NSLog(@"並行隊列裡的同步任務 %@ %d", [NSThread currentThread], i); });}

下面是控制台輸出結果:

2015-11-23 00:40:36.862 01.GCD演練[1952:3613752] 串列隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 0
2015-11-23 00:40:36.863 01.GCD演練[1952:3613752] 串列隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 1
2015-11-23 00:40:36.863 01.GCD演練[1952:3613752] 串列隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 2
2015-11-23 00:40:36.863 01.GCD演練[1952:3613752] 串列隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 3
2015-11-23 00:40:36.863 01.GCD演練[1952:3613752] 串列隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 4
2015-11-23 00:40:36.863 01.GCD演練[1952:3613752] 並行隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 0
2015-11-23 00:40:36.863 01.GCD演練[1952:3613752] 並行隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 1
2015-11-23 00:40:36.863 01.GCD演練[1952:3613752] 並行隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 2
2015-11-23 00:40:36.864 01.GCD演練[1952:3613752] 並行隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 3
2015-11-23 00:40:36.864 01.GCD演練[1952:3613752] 並行隊列裡的同步任務 <NSThread: 0x7ff833505450>{number = 1, name = main} 4

2.非同步作業只在非主線程的線程執行,在串列隊列中非同步作業會在建立的線程中按順序執行。

 dispatch_queue_t q_serial = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL); for(int i = 0; i < 5; ++i){  dispatch_async(q_serial, ^{   NSLog(@"串列隊列 -- 非同步任務 %@ %d", [NSThread currentThread], i);  }); }

因為是非同步作業,所以會建立一個線程。又因為加入到串列隊列中,所以所有的操作只會按順序執行。

2015-11-23 01:03:22.372 01.GCD演練[2081:3627139] 串列隊列 -- 非同步任務 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 0
2015-11-23 01:03:23.373 01.GCD演練[2081:3627139] 串列隊列 -- 非同步任務 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 1
2015-11-23 01:03:24.374 01.GCD演練[2081:3627139] 串列隊列 -- 非同步任務 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 2
2015-11-23 01:03:25.375 01.GCD演練[2081:3627139] 串列隊列 -- 非同步任務 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 3
2015-11-23 01:03:26.376 01.GCD演練[2081:3627139] 串列隊列 -- 非同步任務 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 4

3.非同步作業,並行隊列

 dispatch_queue_t q_concurrent = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT); for(int i = 0; i < 5; ++i){  dispatch_async(q_concurrent, ^{   NSLog(@"並行隊列 -- 非同步任務 %@ %d", [NSThread currentThread], i);  }); }

理論上並行隊列會給每一個非同步作業建立線程,然後讓所有的任務並發執行。但是實際上系統能建立的線程數量是有限的,當建立的線程達到最大線程數以後,後面的非同步作業就需要等待前面的操作執行完畢才能得到執行。哪個線程操作執行完畢,就把等待的非同步任務安排到哪個線程。直到所有的操作執行完畢。你可以把上述代碼的迴圈次數改成5000就可以觀察到此現象。

2015-11-23 01:14:15.282 01.GCD演練[2165:3634728] 並行隊列 -- 非同步任務 <NSThread: 0x7fb3b841b0a0>{number = 4, name = (null)} 3
2015-11-23 01:14:15.282 01.GCD演練[2165:3634724] 並行隊列 -- 非同步任務 <NSThread: 0x7fb3b8514da0>{number = 3, name = (null)} 0
2015-11-23 01:14:15.282 01.GCD演練[2165:3634726] 並行隊列 -- 非同步任務 <NSThread: 0x7fb3b8604db0>{number = 5, name = (null)} 2
2015-11-23 01:14:15.282 01.GCD演練[2165:3634725] 並行隊列 -- 非同步任務 <NSThread: 0x7fb3b86119d0>{number = 2, name = (null)} 1
2015-11-23 01:14:15.285 01.GCD演練[2165:3634729] 並行隊列 -- 非同步任務 <NSThread: 0x7fb3b87011f0>{number = 6, name = (null)} 4

3.NSOperation & NSOperationQueue

雖然GCD的功能已經很強大了,但是它使用的API依然是C語言的。在某些時候,在物件導向的objective-c中使用起來非常的不方便和不安全。

所以蘋果公司把GCD中的操作抽象成NSOperation對象,把隊列抽象成NSOperationQueue對象。


抽象為NSOperation & NSOperationQueue以後的好處有一下幾點:

代碼風格統一了,我們不用在物件導向的objective-C中寫面對過程的C語言代碼了。
我們知道在GCD中操作的執行代碼都是寫在匿名的block裡面,那麼我們很難做到給操作設定依賴關係以及取消操作。這些功能都已經封裝到NSOperation對象裡面了。^-^
NSOperationQueue對象比GCD中隊列更加的強大和靈活,比如:設定並行作業數量,取消隊列中所有操作。
NSOperation分為NSInvocationOperation和NSBlockOperation

NSInvocationOperation的使用

//首先定義一個NSOperationQueue對象NSOperationQueue *queue = [[NSOperationQueue alloc] init];NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@"這裡可以穿參數"];[queue addOperation:op];//把操作排入佇列中即開始執行- (void)operationAction:(id)obj{ NSLog(@"%@ - obj : %@", [NSThread currentThread], obj);}

輸出為:

2015-11-23 02:55:19.067 多線程demo[2604:3686934] <NSThread: 0x7f9dfa443510>{number = 2, name = (null)} - obj : 這裡可以穿參數

NSBlockOperation的使用

NSOperationQueue *queue = [[NSOperationQueue alloc] init];NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ [self operationAction:@"這是NSBlockOperation"];}];[queue addOperation:op];

輸出為:

2015-11-23 02:56:11.812 多線程demo[2617:3687872] <NSThread: 0x7fa983f10a50>{number = 2, name = (null)} - obj : 這是NSBlockOperation

設定依賴關係(執行順序)

 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@"op1"]; NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@"op2"]; //op2在op1之後執行 [op2 addDependency:op1];//這裡需要注意,一定要在addOperation之前設定依賴關係  [queue addOperation:op1]; [queue addOperation:op2];

輸出為:

2015-11-23 02:57:40.283 多線程demo[2661:3689737] <NSThread: 0x7fb663e132d0>{number = 2, name = (null)} - obj : op1
2015-11-23 02:57:40.284 多線程demo[2661:3689737] <NSThread: 0x7fb663e132d0>{number = 2, name = (null)} - obj : op2

沒有設定依賴關係的輸出:

2015-11-23 03:00:45.939 多線程demo[2709:3692307] <NSThread: 0x7fe951d0d8a0>{number = 2, name = (null)} - obj : op2
2015-11-23 03:00:45.939 多線程demo[2709:3692308] <NSThread: 0x7fe951c24720>{number = 3, name = (null)} - obj : op1

到這裡你應該發現了,在NSOperation & NSOperationQueue中,我們不需要再像GCD那樣定義操作的類型和隊列的類型和控制操作的執行順序了,你只需要直接設定操作的執行順序就可以了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.