1.多線程概述編程
程序:由源代碼生成的可執行應用。(eg:QQ.app)網絡
進程:一個正在運行的程序能夠看作一個進程。(eg:正在運行的QQ就是一個進程),進程用用獨立運行所需的所有資源。
session
線程:程序中獨立運行的代碼段。(eg:接收QQ消息的代碼)多線程
一個進程是由一或多個線程組成。進程只負責資源的調度和分配,線程纔是程序真正的執行單元,負責代碼的執行。併發
1.1單線程app
每一個正在運行的程序(即進程),至少包含一個線程,這個線程就叫主線程。async
主線程在程序啓動時被建立,用於執行main函數。函數
只有一個主線程的程序,稱爲單線程程序。性能
在單線程程序中,主線程負責執行程序的全部代碼(UI展示以及刷新,網絡請求,本地存儲等等)。這些代碼只能順序執行,沒法併發執行。優化
1.2多線程
擁有多個線程的程序,稱做多線程程序。
iOS中容許用戶本身開闢新的線程,相對於主線程來說,這些線程,稱做子線程。
能夠根據須要開闢若干子線程。
子線程和主線程都是獨立的運行單元,各自執行互不影響,所以可以併發執行。
1.3單線程和多線程的區別
單線程程序:只有一個線程,即主線程,代碼順序執行,容易出現代碼阻塞(頁面假死)。
多線程程序:有多個線程,線程間獨立運行,能有效的避免代碼阻塞,而且提升程序的運行性能。
注意:iOS中關於UI的添加和刷新必須在主線程中操做。
2.iOS平臺下的多線程
2.1NSThread
方法:
//初始化一個子線程,但須要手動開啓
- (id) initWithTarget : (id) target selector : (SEL)selector object :(id)argument
//初始化一個子線程,並自動開啓
+ (void) detachNewThreadSelector : (SEL) aSelector toTarget : (id) aTarget withObject :(id) anArgument
//開啓子線程
start [operation start]
//取消當前子線程
cancel [operation cancel]
源代碼:
//手動開闢子線程
NSThread *thread = [[NSThread alloc] initWithTarget : self selector:@selector(sayHi) object:nil];
[thread start];
//使用NSThread和NSObject實現的開闢線程,系統會自動釋放,關不關都行
[thread cancel]; 沒有真正取消,而是給線程發送一個信號,經過這個信號進行取消的
[thread exit]; 直接將線程退出
//自動開啓子線程
[NSThread detachNewThreadSelector:@selector(sayHi) toTarget:self withObject:nil];
self.view.backgroundColor = [UIColor redColor] ;
2.2NSObject
//使用performSelectorInBackground開闢子線程
[self performSelectorInBackground :@selector(sayHi) withObject:@"test"];
//在子線程調用的方法裏返回主線程,在調用另外一個方法
[self performSelectoraOnMainThread:@selector(mainThreadChangeColor) withObject :nil waitUntilDone: YES];
- (void)mainThreadChangeColor {
self.view.backgroudColor = [UIColor magentaColor];
}
方法:
獲取當前線程:[NSThread currentThread]
獲取主線程 :[NSThread mainThread]
線程休眠2秒 :[NSThread sleepForTimeInterval:2]
注意:
1.每一個線程都維護這與本身對應的NSAutoreleasePool對象,將其放在線程棧的棧頂。當線程結束時,會清空自動釋放池。
2.爲保證對象的及時釋放,在多線程方法中須要添加自動釋放池。
3.在應用程序打開的時候,系統會自動爲主線程建立一個自動釋放池。
4.咱們手動建立的子線程須要咱們手動添加自動釋放池。
4.NSOperation和NSOperationQueue
NSOperation類,在NVC中屬於M,是用來封裝單個任務相關的代碼和數據的抽象類。
由於它是抽象的,不可以直接使用這個類,而是使用子類(NSInvocation,NSBlockOperation)來執行實際任務。
NSOperation(含子類),只是一個操做,自己無主線程,子線程之分,可在任意線程中使用。一般與NSOperationQueue結合使用。
//NSInvocationOperation:封裝了執行操做的target和要執行的action
//NSBlockOperation:封裝了須要執行的代碼
//NSOperation不能直接進行多線程的建立,須要藉助:NSOperationQueue
//在單獨使用NSOperation的子類去建立線程的時候,必定要啓動才行;
//在使用NSOperation的子類去建立線程的時候,實際上線程沒有真正意義上的建立
源程序:
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test) object:nil];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"我是block");
NSLog(@"+++++%@",[NSThread currentThread]);
NSLog(@"*******%@",[NSThread mainThread]);
}];
- (void)test {
NSLog(@"☺");
NSLog(@"currentThread == %@",[NSThread currentThread]);
NSLog(@"mainThread == %@",[NSThread mainThread]);
}
//藉助隊列,建立子線程
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
[queue addOperation:blockOperation]; //有addOperation的時候,就不能夠用start,不然會形成崩潰
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//建立隊列的對象
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//最大的併發數量值
//當值設置爲1的時候,能夠叫作串行(單線程)順序執行
queue.maxConcurrentOperationCount = 2;
//當值設置大於1的時候,叫作並行:多條通道同時進行各自的任務
for (int i = 0; i < 10; i++) {
//建立10個線程
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"currentThread == %@,mainThread == %@,%d",[NSThread currentThread],[NSThread mainThread],i);
}];
[queue addOperation:blockOperation];
}
}
5.GCD(Grand Central Dispatch)使用
簡介:GCD是Apple開發的一種多核編程技術。主要用於優化應用程序以支持多核處理器以及其餘對稱多處理系統。
GCD提供函數實現多線程開發,性能更高,功能也更強大。
它首次發佈在Mac OS X 10.6,iOS4及以上也能夠用。
任務:具備必定功能的代碼段。通常是一個block或者函數。
分發隊列:GCD以隊列的方式進行工做,FIFO(先進先出)
GCD會根據分發隊列的類型,建立合適數量的線程執行隊列中的任務。
GCD中兩種隊列:
1.SerialQueue:一次只執行一個任務。Serial queue一般用於一般用於同步訪問特定的資源或數據。當你建立多個Serial queue時,雖然他們各自是同步執行的,但Serial queue與Serial queue之間是併發執行的。
2.Concurrent:能夠併發地執行多個任務,可是遵照FIFO
#pragma mark - 使用GCD去建立一個串行隊列
//第一種:系統提供的建立串行隊列的方法
dispatch_queue_t queue = dispatch_get_main_queue();
//在真正的開發中若是須要建立串行隊列,比較習慣用這種
//第二種:本身去建立
dispatch_queue_t = dispatch_queue_create(DISPATCH_QUEUE_SERIAL,0);
#pragma mark - 使用GCD去建立並行隊列
//第一種:系統的方式
//參數1:優先級(有4個,沒有明顯的區別)
dispatch_queue_t queue = dispatch_get_globel_queue(DISPATCH_QUEUE_PRIORITY);
//第二種:本身定義的方式(參數和串行不同)
//參數1:表示建立隊列的名字
//參數2:系統提供的宏
//建立隊列
dispatch_queue_t queue = dispatch_queue_create("myQueue",DISPATCH_QUEUE_CONCURRENT);
//建立任務
dispatch_async(queue,^{
NSLog(@"current == %@",[NSThread currentThread]);
NSLog(@"main == %@",[NSThread MainThread]);
NSLog(@"我是任務一,子進程一");
});
dispatch_async(queue,^{
NSLog(@"current == %@",[NSThread currentThread]);
NSLog(@"main == %@",[NSThread MainThread]);
NSLog(@"我是任務二,子進程二");
});
dispatch_async(queue,^{
NSLog(@"current == %@",[NSThread currentThread]);
NSLog(@"main == %@",[NSThread MainThread]);
NSLog(@"我是任務三,子進程三");
});
#pragma mark - 幾秒以後去作每一件事
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(3.0 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
NSLog(@"3.0秒的時候");
}
);
#pragma mark - 重複向一個隊列中添加多個任務
dispatch_queue_t queue = dispatch_queue_create(0,DISPATH_QUEUE_CONCURRENT);
dispatch_apply(100,queue,^(size_t index)) {
NSLog(@"index = %zu",index);
}
#pragma mark - 分組
//建立一個分組
dispatch_group_t group = dispatch_group_create();
//建立一個並行隊列
dispatch_queue_t queue = dispatch_queue_create(0,DISPATCH_QUEUE_CONCURRENT);
//建立任務1
dispatch_group_asunc(group,queue,^{
NSLog(@"我是任務1");
});
//建立任務2
dispatch_group_asunc(group,queue,^{
NSLog(@"我是任務2");
});
//建立任務3
dispatch_group_asunc(group,queue,^{
NSLog(@"我是任務3");
});
//用於監聽全部任務的執行狀況的,因此此功能代碼必須放在全部任務以後進行書寫的
dispatch_group_notify(group,queue,^{
NSLog(@"我是監聽的,最後執行");
});
#pragma mark - 併發中的串行(披着羊皮的狼)
//建立的串行隊列
dispatch_queue_t queue = dispatch_queue_create(0,DISPATH_QUEUE_CONCURRENT);
dispatch_async(queue,^{
NSLog(@"任務1");
});
dispatch_async(queue,^{
NSLog(@"任務2");
});
dispatch_async(queue,^{
NSLog(@"任務3");
});
#pragma mark - loadData
//1.url
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
//2.session
NSURLSession *session = [NSURLSession sharedSession];
//3.task
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *_NUllabel data,NSURLResponse *_Nullabel response,NSError *_Nullable error){
if(error == nil){
//處理數據
//回到主線程刷新UI
dispatch_async(dispatch_get_main_queue(),^{
//本身靈活的寫點東西吧!!!
})
}
}];