主線程的做用 (在主線程中才能設置)ios
顯示/刷新UI界面程序員
處理UI事件(好比點擊事件、滾動事件、拖拽事件);編程
主線程的使用注意安全
別將比較耗時的操做放到主線程中。多線程
耗時操做會卡住主線程。影響體驗。併發
[NSThread currentThread]得到當前線程。app
打印線程。num屬性顯示有多少條線程。異步
將耗時操做放在子線程中(後臺線程,非主線程);async
多線程好處在哪?函數
在用戶點擊按鈕那一刻就有反應
能同時處理耗時操做和用UI控件的事件。
多線程技術:
1,pthread 一套通用的多線程API(幾乎不用)
適用於Unix\Linux\Windows等系統
跨平臺\可移植
使用難度大。
須要程序員來管理線程的生命週期。
建立一個線程用
pthread_create(&pthread_t,NULL,(void *)(*)(void *),NULL);便可
2,NSThread 使用OC,(程序員管理)
使用更加面向對象
簡單易用,可直接操做線程對象
建立一個線程用 [NSThread alloc] init……
[self start] 開啓;
selector(mainThread) 返回主線程;
selector(isMainThread) 是不是主線程;
selector (currentThread); 判斷當前線程;
能夠設置線程優先級,
優先級越高,CUP的佔有率越高。
線程有name屬性,能夠設置名字。
建立線程第2種方式:
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
分離一條線程,沒有返回值。
建立線程第3種方式。
[self performSelectorInBackground:@selector(run:) withObject:@"abc參數"];
每個時刻只有一個線程在執行。
[thread start]後進入一個可調度線程池(有機會被調度。狀態爲就緒或運行);
start
new ----》 就緒 -----》運行
《------
調用了sleep方法或等待同步鎖 後就進入 阻塞狀態(清出線程池) 條件知足後又回到調度池。
調用stop 線程進入的是dead 死亡狀態。再也沒有了。
+(void)sleepUntilDate:(NSDate *)date;
+(void) sleepForTimeInterval:(NSTimeInterval)ti;
線程運行完以後就會死亡, 不能能再次調用。
[thread exit]線程退出。
3,GCD C
旨在替代NSThread等線程技術
充分利用設備的多核。(自動)
不用管理線程。
GCD有2個核心概念
任務:執行什麼樣的操做。
隊列:用來存聽任務。
GCD的使用就2個步驟。
定製任務
確認想作的事情。
將任務添加到隊列中
GCD會自動將隊列中的任務取出,放到對應的線程中執行。
任務取出遵循隊列的FIFO原則,先進先出。後進後出
調用libdispach庫才行
都是懿dispatch開頭。
1,用同步的方式來執行任務。(有順序執行)
dispatch_sync(queue,block);同步執行
queue:隊列
block:任務
2,用異步的方式執行任務(在另外一條線程中執行)
dispatch_async(queue,block);
GCD隊列分2大類;
併發隊列;
可讓任務同時執行。
串行隊列
用函數給隊列賦值任務;
隊列分爲並行和串行。
函數分爲同步和異步。
異步函數加並行隊列 == 開一條子線程。
同步:在當前線程中執行任務,不具有開啓新線程能力
異步:在新的線程中執行任務,具有開啓新線程的能力。
併發和串行決定了任務的執行方式。
併發:多個任務併發執行。 調用dispatch_queue_t queue = dispatch_get_global_queue(優先級,隨意值);
添加任務到隊列中,執行任務。
串行:一個任務執行完畢後,再執行下一個任務。
serial 英文,連續,串行的意思。。
dispatch_queue_t queue = dispatch_queue_create("com.itheima,queue",隊列的類型);
隊列名稱在調試的時候能夠看的到代碼在哪一個隊列裏面執行。
調用async函數會開啓一個線程,可是若是函數的隊列是串行的就不會開更多了。每加一個並行隊列開啓線程數會加1.
調用sync函數不會開線程。並行和串行隊列都同樣
MRC裏面釋放用dispatch_release(queue);
ARC裏面不能用,會自動釋放。
即便在ARC中
凡是函數名中帶有create\copy\new\retain等字眼返回值的,都須要在不使用這個數據的時候進行release(有的話都寫一下,報錯就不寫了)
GCD數據類型則不用,release。
CF(Core foundation)的數據類型在ARC環境下仍是得須要再作release;
系統自帶的用get ,本身創的用create 。(蘋果規範);
主線程隊列是不會開新線程的。
別在主線程中用同步函數添加主隊列,會造成相互等待,死鎖。
用異步函數能夠(異步能夠延緩執行,同步要立刻執行)。
同步會在當前進程中執行,會停下來。異步會本身開一個線程。與當前線程併發執行,(只要隊列不是主隊列)
iOS常見的延時執行有2種方式;
1,[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
2秒以後調用run方法;
2,GCD方式
dispatch_after();//有個隊列參數,表示多少秒後在哪一個線程中執行。
//1.聲明在哪一個隊列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.計算任務執行的時間
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
// 3.會在when這個時間點, 執行queue中的任務
dispatch_after(when, queue, ^{ //會另外聲明一個隊列。
NSLog(@"----run----%@", [NSThread currentThread]);
});
須要整個程序執行過程當中只能打印一次時。
static dispatch_once_t onceToken;
能夠用 dispatch_once(&onceToken,^{
NSLog(@"--------touchesBegin");
});
隊列組,:
有這麼一種需求。
等2個線程都執行完以後再到主線程中執行操做。
就能夠用隊列組把2個線程封裝。執行隊列組。
建立group//比串行執行要快不少。。。
dispatch_group_t group = dispatch_group_create();
建立隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
把任務和隊列放到異步組函數中
*/
dispatch_group_async(group, queue, ^{
for (int i=0; i<100; i++) {
NSLog(@"執行線程一:%d",i);
}
});
dispatch_group_async(group, queue, ^{
for (int i=0; i<100; i++) {
NSLog(@"執行線程二:%d",i);
}
});
//線程組執行完成後執行:(重點,沒有它,要組也沒啥用了)
dispatch_group_notify(group, queue, ^{
NSLog(@"線程組執行完成");
});
//下面是重複執行:10表明重複執行次數。中間是執行隊列。index是第幾回執行;(注意執行是併發(這是與for循環的區別));
dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
//index表示是第幾回執行
NSLog(@"執行第%zu次",index);
});
柵欄barrier ,用來將線程分開,執行完上面的線程到執行它再到執行後面的線程;(有隊列queue關鍵字,因此就是把queue隊列的隔開,其它隊列可不行,中間執行)
dispatch_barrier_async(queue, ^{
NSLog(@"barrier");
});
4,NSOperation OC
基於GCD
比GCD多了一些簡單實用的功能
使用更加面向對帶的用get ,本身創的用create 。(蘋果規範);
, 配合使用NSOperation NSOperationQueue也能實現多線程編程
,NSOperation和NSOperationQueue 實現多線程的具體步驟
先將須要執行的操做封裝到一個NSOperation 對象中。
而後將NSOperation對象添加到NSOperationQueue中
系統會自動將NSOperationQueue中的NSOperation取出來
將取出的NSOperation封裝的操做放到一條新線程中執行。
NSOperation是一個抽象類,並不具有封裝操做的能力,必須使用它的子類。
使用NSOperation子類的方式有3種。
NSInvocationOperation
NSBlockOperation
自定義的子類繼承NSOperation,實現內部相應的方法。
NSInvocationOperation
建立操做對象,封裝要執行的任務。
NSInvocationOperation * operation = [ alloc] initwithTarget…… 任務是方法;
能夠[operation start]調用,可是這樣是會在當前線程中調用,並沒有啥用,仍是得放到方法中去;
NSBlockOperation
使用類方法來聲明
NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock^{ }]; 任務是block ,NSBlockOperation 是block的封裝
也能夠 直接用start 調用,但也無啥用。
有方法: [operation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
只要operation封裝的任務大於1,就會異步執行。
調用了addExecutionBlock 的話 operation的任務就有多個。 用start是會多線程執行的。
-(void) invocationOperation
{
}
NSOperatinQueue * queue =[ [NSOperationQueue alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];//這樣直接就會是異步執行。 (併發有着不肯定性。可是機率是會有的)
隊列能夠設置 最大併發數, queue.maxConcurrentOperationCount = 1; 表示在這個隊列中最多同時執行的併發任務數爲1;
(不要設置太多,否則可能會卡, 2-3就好,5之內,否則UI會卡);
- (void) cancelAllOperations; 隊列的所有線程都停掉
也能夠調用NSOperation 的-(void)cancel 方法取消單個操做。
-(void)setSuspended:(BOOL)b;//YES 表明暫停隊列。
//(一個比較好的地方是 : cancel表明取消的意思,suspended,表示已經掛起,這些英文記住了用來命名挺好的,看看別人的過去時都是加ed的,就是這麼強,因此語法很差的話,多關注ios的命名規則時很好的);
當下載時,同時開啓多條子線程,當用戶操做UI時會發生卡頓現象,那麼咱們就能夠在用戶滾動時掛起隊列。不操做時就繼續隊列/
下載時同時開啓多條線程下載會比較快。
有個有趣的地方是 NSOperation 竟然也有一個隊列優先級, 我了個去, 不愧是GCD的封裝啊 ,就是那麼強大。
還能夠設置依賴。來保證執行的順序;
[operationB addDependency: operationA]; B依賴A 那麼B只能在A以後執行。(靈活應用能夠作不少事情。)
A-》B B -》C C—》A
當心相互依賴問題。
能夠在不一樣的queue的NSOperation之間建立依賴關係。可是不能相互依賴。
操做的監聽。
operation.completionBlock = ^{
//下載完圖片後想作的事情。
};
自定義Operation(繼承自Operation)
{
要在operation裏面實現 -(void)main{}方法
}
多線程安全隱患:1,一塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一個資源
好比說多個線程訪問同一個對象、同一個變量、同一個文件。
使用線程鎖來解決,同一時間只能有一個線程訪問。