多線程技術
process程序:在系統中運作的一個應用程式;每個程序都是獨立的;有專有的記憶體空間
thread線程:程式的一段執行序列,程序的不部分;
特點:1、程序隻配置設定記憶體空間,并不執行任務
2、每個程序至少有一個線程,該線程叫做主線程
3、線程是程序的基本執行單元,程序的所有任務都是線上程中執行
4、每個線程中得任務的執行都是串行的
多線程并發:一個程序中多條線程并發執行任務;
特點: 1、提高程式的執行效率,提高資源使用率
2、同一時間,CPU隻能處理一條線程
3、多線程并發時,CPU在多條線程間快速切換
4、線程切換的 速度很快,就造成了多線程的并發執行
5、開啟線程需要記憶體空間,線程太多會造成排程消耗太多資源
6、線程過多會降低每條線程被排程的頻率(線程執行效率降低)
多線程的應用:
主線程:顯示重新整理UI界面、處理UI事件;耗時任務(如下載下傳)放在子線程中
判定方法在哪個線程中執行:NSLog(@“目前的線程:”,[NSThread currentThread]);
四種多線程技術:pthread NSThread GCD NSOperation
1.pthread:
基于c語言的API ,可編寫多平台應用,使用難度大,需要手動建立銷毀線程,可自定義功能少
pthread_t pthread;
void *task(void *data){
NSLog(@“目前子線程:%@“,NSThread currentThread];
return 0;
}
pthread_create(&pthread,NULL,task,NULL);
2.NSThread:
- 基于OC,相對于pthread使用簡單,面向對象
- 缺點:需要自己管理線程的生命周期,線程同步,加鎖、開鎖,管理多個線程比較困難
手動建立線程的三種方法:
1、NSThread執行個體方法:- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument NS_AVAILABLE(10_5, 2_0);(隻建立)
2、NSThread類方法:+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument; (建立并運作)
3、NSThread執行個體方法:- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg NS_AVAILABLE(10_5, 2_0);
注:selector: 線程執行的方法,這個selector隻能由一個參數,而且沒有傳回值
object: 傳給target的唯一參數,也可以為nil
target: selector消息發送的對象 隻有第一個能設定名字、優先級
線程阻塞(延遲):
- // sleep⽅法⼀:開始睡眠5秒鐘
[NSThread sleepForTimeInterval:5];
- // sleep⽅法二:
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:3
[NSThread sleepUntilDate:date];
強制停止線程:
[NSThread exit];
其他常用方法:
- +(NSThread *)mainThread; //獲得主線程
- -(BOOL)isMainThread;//判斷是否是主線程
- +(double)threadPriority;//擷取線程優先級 優先級取值範圍0.0~1.0 值越大優先級越高
- +(BOOL)setThreadPriority:(double)p;//設定優先級
- -(NSString*)name;//擷取線程名字
- -(void)setName:(NSString*)n;//設定線程名字
線程的狀态:
- New(建立) -> Runnable(就緒) --> cpu開始排程目前線程 ---> Running(運作) ---> 調用 sleep方法 ---> Blocked(阻塞) ---> 移除線程池 ---> sleep執行完畢/到時 ---> 進入線程 池 ---> Runnable(就緒)
- 如果線程任務執行完畢/異常/強制退出 ---> Dead(死亡)
加鎖:
- 盡量避免使用@synchronized,也就是避免多個線程通路同一個資源,因為有了加鎖、 解鎖需要消耗比較大的cpu資源
- 加鎖的前提:多個線程同時通路同一個資源的時候才需要加鎖(互斥鎖)
- 線程同步:多條線程在同一條線上執行(按順序地執行任務)
- 盡量講加鎖、資源搶奪的業務邏輯交割伺服器端處理,減少移動用戶端的壓力
synchronized關鍵字
1、synchronized關鍵字的作用域有二種:
1)是某個對象執行個體内,synchronized aMethod(){}可以防止多個線程同時通路這個對象的synchronized方法(如果一個對象有多個synchronized方法,隻要一個線程通路了其中的一個synchronized方法,其它線程不能同時通路這個對象中任何一個synchronized方法)。這時,不同的對象執行個體的 synchronized方法是不相幹擾的。也就是說,其它線程照樣可以同時通路相同類的另一個對象執行個體中的synchronized方法;
2)是某個類的範圍,synchronized static aStaticMethod{}防止多個線程同時通路這個類中的synchronized static 方法。它可以對類的所有對象執行個體起作用
2、除了方法前用synchronized關鍵字,synchronized關鍵字還可以用于方法中的某個區塊中,表示隻對這個區塊的資源實行互斥通路。用法是: synchronized(this){},它的作用域是目前對象;
原子和非原子屬性:
- nonatomic: 非原子 —> 不會在set方法中加鎖,這個是推薦方式,會占用資源少
- atomic:原子 —> 在set方法中加鎖,防止多個線程同時執行set方法
線程間的通訊:
1.在一個程序中,線程往往不是孤立存在的,多個線程之間需要經常進行通信
2.線程間通訊的方式:
- 一個線程傳遞資料給另一個線程
- 在一個線程中執行完特定任務後,轉到另一個線程繼續執行任務
子線程傳回主線程的兩種方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait
***********************************************************************************************
3. GCD
并行隊列隻有在執行多個異步任務的時候展現出來并發現象,因為線上程快速切換時展現出并行;
同步執行的任務 會緻使後面的任務送出阻塞;
GCD:Grand Central Dispath 大中央排程
- GCD的基本思想就是将操作(任務)放在隊列中去執行
- 隊列負責排程任務執行所在的線程以及具體的執行時間
- 隊列的特點是先進先出,新添加至隊列的操作(任務)都會排在隊尾
GCD的函數都是以dispatch開頭的,dispatch的意思是“配置設定、排程”
- 串行隊列中的任務會按順序執行
- 并行隊列中的任務通常會并發執行,而且無法确定任務的執行順序
dispatch_async表示異步操作,異步操作會新開辟線程來執行任務,而且無法确定任務的執行順序 dispatch_sync表示同步操作,同步操作不會新開辟線程
- 在串行隊列中執行同步任務:不會建立線程,按順序執行任務(毫無用處)
- 在串行隊列中執行異步任務,會建立線程,按順序執行任務(非常有用)
- 在并行隊列中執行同步任務:不會建立線程,按順序執行任務(幾乎沒用)
- 在并行隊列中執行異步任務:會建立多個線程,但是無法确定任務的執行順序(有用,但是很容易出錯)
全局隊列:
- 全局隊列是系統的,直接拿過來就可以用,與并行隊列類似,但是不能指定隊列的名字,調試時無法确認任務所在隊列
- 在全局隊列中執行同步任務:不會建立線程,按順序執行任務
- 在全局隊列中執行異步任務:會建立多個線程,但是無法确定任務的執行順序
主隊列:
- 如果把任務放到主隊列中進行處理,那麼不論處理函數是異步的還是同步的都不會開啟新的線程。
- 每一個應用程式隻有一個主線程即隻有一個主隊列
- 為什麼需要再主線程上執行任務呢?因為在ios開發中,所有UI的更新任務都必須在主線程上執行。
- 主隊列中的操作都是在主線程中執行的,不存在異步的概念
- 在主線程中向 主隊列中添加的同步操作會死鎖
線程阻塞(延遲):三種方法
1 //過3秒後做一件事兒,這個做法會導緻主線程阻塞,不提倡使用
2 - (IBAction)sleepDelay:(id)sender
3 {
4 NSLog(@"3秒後幹件事兒!");
5 [NSThread sleepForTimeInterval:3];
6 NSLog(@"幹事兒中...");
7 }
8 - (IBAction)performAfterDelay:(id)sender
9 {
10 NSLog(@"3秒後幹件事兒!");
11 //在3秒後會啟動一個線程去完成任務(調用方法task),此方法并不會阻塞
12 [self performSelector:@selector(task) withObject:nil afterDelay:3];
13 NSLog(@"主線程繼續向下執行...");
14 }
15 - (void)task
16 {
17 NSLog(@"幹活中...");
18 }
19 //使用GCD實作延遲執行
20 - (IBAction)dispatchAfter:(id)sender
21 {
22 /*
23 1秒 = 1000毫秒
24 1毫秒 = 1000微秒
25 1微秒 = 1000納秒
26 */
27 //dispatch_time函數的第二個參數機關是納秒
28 NSLog(@"3秒鐘後做事");
29 dispatch_after(
30 /**
31 * 延遲的函數
32 *
33 * @param DISPATCH_TIME_NOW 從現在起
34 * @param int64_t 延遲描述
35 * NSEC_PER_SEC 這個宏的意思是每秒多少納秒
36 * @return 無
37 */
38 dispatch_time(DISPATCH_TIME_NOW, (int64_t)3 * NSEC_PER_SEC),
39 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
40 ^{
41 NSLog(@"3秒後我開始做需要的事情啦!");
42 });
43 }
View Code
線程間的通訊:
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執⾏耗時的異步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主線程,執⾏UI重新整理操作
});
});
dispatch_once 參照:線程安全的單例 http://www.cnblogs.com/ChrisYu/p/4651114.html
小結:
無論什麼隊列和什麼任務,線程的建立和回收不需要程式員參與,由隊列來負責,程式員隻需要面對隊列和任務。GCD在後端管理這一個線程池,GCD不僅決定着Block代碼塊将在哪個線程中被執行,而且還可以根據可用的系統資源對這些線程進行管理,進而讓開發者從線程管理的工作中解放出來,通過GCD這種集中的管理線程,緩解了大量的線程被建立的問題
4. NSOperation
單詞:
1. global ge(0) lao(1) bao(3) 全局
2. concurrent 并發
3. queue 隊列
作業:
1. 從網上下載下傳圖檔并顯示到界面上。
要求,下載下傳過程中界面不能死。
用GCD做
2. 了解一個第三方架構SDWebImage
1 - (IBAction)serialOperation
2 {
3 //預設OperationQueue中的線程都是并行的
4 NSOperationQueue *queue = [[NSOperationQueue alloc]init];
5 NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil];
6 // [operation1 start];//不會啟動線程,直接在主線程中執行
7 [queue addOperation:operation1];
8 NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil];
9 //[operation2 start];
10 //線程二要依賴線程一,是以線程2會等線程一結束了再執行,按此方式可以讓兩個線程串行執行
11 [operation2 addDependency:operation1];
12 [queue addOperation:operation2];
13 }
14
15 - (void)printPlusSignal
16 {
17 for (int i=0; i<10; i++) {
18 [NSThread sleepForTimeInterval:1];
19 NSLog(@"++++++++++++++");
20 }
21 }
22
23 - (void)printMinusSignal
24 {
25 for (int i=0; i<10; i++) {
26 [NSThread sleepForTimeInterval:1];
27 NSLog(@"----------------");
28 }
29 }
30
31 - (IBAction)concurrentQueue:(id)sender
32 {
33 //預設OperationQueue中的線程都是并行的
34 NSOperationQueue *queue = [[NSOperationQueue alloc]init];
35 NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil];
36
37 [queue addOperation:operation1];
38 NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil];
39
40 [queue addOperation:operation2];
41 }
42
43 //使用Block送出任務給OperationQueue
44 - (IBAction)blockQueue
45 {
46 NSOperationQueue *queue = [[NSOperationQueue alloc]init];
47 //CallBack 回調
48 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
49 for (int i=0; i<10; i++) {
50 [NSThread sleepForTimeInterval:1];
51 NSLog(@"++++++++++++++");
52 }
53 }];
54 [queue addOperation:operation1];
55
56 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
57 for (int i=0; i<10; i++) {
58 [NSThread sleepForTimeInterval:1];
59 NSLog(@"-------------");
60 }
61 }];
62 [queue addOperation:operation2];
63 }
64 - (IBAction)mainQueue
65 {
66 NSOperationQueue *queue = [[NSOperationQueue alloc]init];
67 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
68 NSLog(@"在子線程中-----%@", [NSThread currentThread]);
69 //擷取主隊列(回到主線程)
70 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
71 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
72 NSLog(@"在主線程中?-----%@", [NSThread currentThread]);
73 }];
74 [mainQueue addOperation:operation2];
75 }];
76 [queue addOperation:operation];
77 }
View Code
轉載于:https://www.cnblogs.com/ChrisYu/p/4649968.html