1.進程與線程
進程是具有一定獨立功能的程序關于某個數據集合上的一次運行活動,是操作系統進行資源分配和調度的一個獨立單位。線程是進程的一個實體,是CPU調度和分派的基本單位,是比進程更小的能獨立運行的基本單位。任務(task)用于指代抽象的概念,表示需要執行工作,具體可以是一個函數或者一個block。
2.多線程實現方式
- POSIX線程:簡稱Pthreads,是線程的POSIX標準,該標準定義了創建和操作線程的一套API,是由C語言實現的,例如pthreadcreate()函數可以創建線程,pthreadexit()函數可以終止當前線程,pthreadmutexinit()函數可以初始化互斥鎖等等,使用這種方式操作線程需要扎實的C編程功底以及對系統底層工作機制的認識。
- NSThread類:線程,此種方式是輕量級的線程機制,但需要自行管理線程的生命周期,線程同步等問題,同步鎖會產生系統開銷。
- NSOperationQueue類:執行隊列,優點是不需要關心線程管理,數據同步的事情,可以把精力放在線程需要執行的操作上。
- GCD:Grand Central Dispatch,是蘋果公司開發的技術,以優化的應用程序支持多核心處理器和其他的對稱多處理系統的系統。這建立在任務并行執行的線程池模式的基礎上的。它首次發布在Mac OS X 10.6,iOS 4及以上也可以使用,該技術是取代上述技術的技術。
3.NSThread
輕量級最低,相對簡單,需要手動管理所有的線程活動(生命周期,休眠,同步等)線程同步對數據的加鎖會有一定的系統開銷。
(1)創建線程
a.//類方法創建
[NSThread detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument];
b.//對象方法創建
NSThread *thread = [[NSThread alloc] initWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument];
[thread start];
(2)線程同步
a.NSLock
b.NSCondition
c.@synchronized代碼塊
4.NSOperation
自帶線程周期管理,可只關注自己處理邏輯
NSOperation是面向對象的抽象類,實現只能是其子類(NSInvocationOperation和NSBlockOperation),對象需要添加到NSOperationQueue隊列里執行
a.同步執行
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(foo) object:nil];
[operation1 start];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
}];
[operation2 start];
b.異步執行
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(foo) object:nil];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];
//如果需要控制隊列中線程的數量,可以使用下面的方式:
[queue setMaxConcurrentOperationCount:5];
5.GCD
GCD全稱為Grand Central Dispatch,是libdispatch的市場名稱,而libdispatch是Apple的一個庫,其為并發代碼在iOS和OS X的多核硬件上執行提供支持。確切地說GCD是一套低層級的C API,通過 GCD,開發者只需要向隊列中添加一段代碼塊(block或C函數指針),而不需要直接和線程打交道。GCD在后端管理著一個線程池,它不僅決定著你的代碼塊將在哪個線程被執行,還根據可用的系統資源對這些線程進行管理。這樣通過GCD來管理線程,從而解決線程被創建的問題。
異步隊列 VS 同步隊列
dispatch_async(queue,block) async 異步隊列,dispatch_async 函數會立即返回, block會在后臺異步執行。 dispatch_sync(queue,block) sync 同步隊列,dispatch_sync 函數不會立即返回,及阻塞當前線程,等待 block同步執行完成。
//例:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL * url = [NSURL URLWithString:@"http://image.jpg"];
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
UIImage *image = [[UIImage alloc]initWithData:data];
if (data != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
}
});
串行隊列和并行隊列
串行隊列就是單條線程中,任務挨個處理,主線程本身就是串行隊列,一條鐵路多個火車排隊走。 并行隊列就是多條線程,多個任務同時處理,多條鐵軌多個火車同時走。
//串行隊列
dispatch_queue_t queue = dispatch_queue_create("baobao", DISPATCH_QUEUE_SERIAL);
//并行隊列
dispatch_queue_t queue = dispatch_queue_create("baobao", DISPATCH_QUEUE_CONCURRENT);
//系統默認有一條并行隊列
dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//系統默認有一條串行隊列
dispatch_queue_t mainQ = dispatch_get_main_queue();
私有隊列和全局隊列
//私有隊列
dispatch_queue_t queue = dispatch_queue_create("baobao", NULL);
//全局隊列有且唯一,統一調度,優化資源
dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
使用隊列
// 提交一個block
dispatch_async(queue, ^{
//...
dispatch_async(dispatch_get_main_queue(), ^{
//刷新UI
});
});
//通常的,無特殊要求的,分線程一律采用global_queue統一調度,優先級無特殊要求的采用Default即可。所謂的優先級分配是指待分配任務中高優先級先行,而對于已進入線程的任務,不會受到后續任務的優先級影響。
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
組任務
(1)dispatch_group_async 可以實現監聽一組任務是否完成,完成后得到通知執行其他的操作。這個方法很有用,比如你執行三個下載任務,當三個任務都下載完成后你才通知界面說完成的了。例:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"group1 [NSThread sleepForTimeInterval:3];");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"group2 [NSThread sleepForTimeInterval:2];");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"group3 [NSThread sleepForTimeInterval:1];");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"main thread.");
});
執行結果:
**2017-01-03 10:29:50.128 ****簡書****[960:412447] group3 [NSThread sleepForTimeInterval:1];**
**2017-01-03 10:29:51.130 ****簡書****[960:412448] group2 [NSThread sleepForTimeInterval:2];**
**2017-01-03 10:29:52.128 ****簡書****[960:412450] group1 [NSThread sleepForTimeInterval:3];**
**2017-01-03 10:29:52.129 ****簡書****[960:412106] main thread.**
(2)dispatch_barrier_async是在前面的任務執行結束后它才執行,而且它后面的任務等它執行完成之后才會執行,例:
dispatch_queue_t queue = dispatch_queue_create("baobao", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"dispatch_async1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"dispatch_async2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:1];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"dispatch_async3");
});
執行結果:
**2017-01-03 10:36:38.500 ****簡書****[1033:454725] dispatch_async2**
**2017-01-03 10:36:38.500 ****簡書****[1033:454746] dispatch_async1**
**2017-01-03 10:36:38.500 ****簡書****[1033:454746] dispatch_barrier_async**
**2017-01-03 10:36:42.511 ****簡書****[1033:454746] dispatch_async3**
//如果使用dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
執行結果:
**2017-01-03 10:39:45.198 ****簡書****[1059:477846] dispatch_barrier_async**
**2017-01-03 10:39:48.199 ****簡書****[1059:477847] dispatch_async2**
**2017-01-03 10:39:48.199 ****簡書****[1059:477849] dispatch_async1**
**2017-01-03 10:39:48.199 ****簡書****[1059:477894] dispatch_async3**
//說明dispatch_barrier_async的順序執行還是依賴queue的類型,必需要queue的類型為dispatch_queue_create創建的,而且attr參數值必需是DISPATCH_QUEUE_CONCURRENT類型,前面兩個非dispatch_barrier_async的類型的執行是依賴其本身的執行時間的,如果attr如果是DISPATCH_QUEUE_SERIAL時,那就完全是符合Serial queue的FIFO特征了。
線程批處理
dispatch_apply的作用是在一個隊列(串行或并行)上“運行”多次block,其實就是簡化了用循環去向隊列依次添加block任務。
//創建異步串行隊列
dispatch_queue_t queue = dispatch_queue_create("baobao", DISPATCH_QUEUE_SERIAL);
//運行block3次
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"apply loop: %zu", i);
});
NSLog(@"After apply");
執行結果:
**2017-01-03 10:46:59.132 ****簡書****[1088:522375] apply loop: 0**
**2017-01-03 10:46:59.133 ****簡書****[1088:522375] apply loop: 1**
**2017-01-03 10:46:59.133 ****簡書****[1088:522375] apply loop: 2**
**2017-01-03 10:46:59.133 ****簡書****[1088:522375] After apply**
dispatch_once函數
保證整個應用程序生命周期中某段代碼只被執行一次!dispatch_once_t必須是全局或static變量,非全局或非static的dispatch_once_t變量在使用時會產生BUG
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
掛起和恢復隊列
dispatch_queue_t queue = dispatch_queue_create("baobao", DISPATCH_QUEUE_SERIAL);
//提交第一個block,延時3秒打印。
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"After 3 seconds...");
});
//提交第二個block,也是延時3秒打印
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"After 3 seconds again...");
});
//延時一秒
NSLog(@"sleep 1 second...");
[NSThread sleepForTimeInterval:1];
//掛起隊列
NSLog(@"suspend...");
dispatch_suspend(queue);
//延時5秒
NSLog(@"sleep 5 second...");
[NSThread sleepForTimeInterval:5];
//恢復隊列
NSLog(@"resume...");
dispatch_resume(queue);
//執行結果:
**2017-01-03 10:59:51.390 ****簡書****[1123:585605] sleep 1 second...**
**2017-01-03 10:59:52.391 ****簡書****[1123:585605] suspend...**
**2017-01-03 10:59:52.392 ****簡書****[1123:585605] sleep 5 second...**
**2017-01-03 10:59:54.395 ****簡書****[1123:585650] After 3 seconds...**
**2017-01-03 10:59:57.393 ****簡書****[1123:585605] resume...**
**2017-01-03 11:00:00.393 ****簡書****[1123:585650] After 3 seconds again...**