多線程基礎知識

http://www.cnblogs.com/dyf520/p/3805297.html
 
 
進程:正在運行的程序
內存:每一個進程所佔的存儲空間
線程:1個進程要像執行任務,必須得有線程,線程是進程的基本執行單元,
 
線程的串行:
·1個線程中人物的執行是串行的
·0同一個時間內,1個線程只能執行1個任務
0·線程是進程的一條執行路徑
 
--------多線程
·一個進程中能夠開啓多條線程,每條線程能夠並行(同時)同時執行不一樣的任務
·進程-》車間  線程-》車間工人
線程的並行:
·進程內多個線程同時執行,可提升程序的執行效率
 
---多線程的原理
·同一時間,cpu只能處理一條線程,只有1條線程在工做(執行)
·多線程併發(同時)執行,實際上是cpu快速的在多條線程之間調度(切換)
·若cpu調度線程的時間足夠快,就形成了多線程併發執行的假象
·思考:線程很是多,會發生什麼:
》cpu繪製n多線程之間調度,cpu會累死,消耗大量的cpu資源
》每條線程被調度執行的頻次會下降(線程的執行效率下降)
 
---多線程的優缺點
-·多線程的優勢:
·1.能是低昂提升程序的執行效率
·2.能適當提升資源利用率(cpu,內存利用率)
 
-·多線程的缺點:
·1.開啓線程須要佔用必定的內存空間,若開啓大量現場,會佔用大量的內存空間,下降程序性能
·2.線程越多,cpu在調度線程刪搞定開銷就越大
·3.程序設計更加複雜:好比線程之間的通訊、多線程的數據共享
 
-------------------------多線程在iOS開發中的的應用--------
·一。主線程
1.一個iOS程序運行後,默認會開啓1條線程,稱爲「主線程」or「UI線程」
 
2.主線程的主要做用
·顯示、刷新UI界面
·處理UI事件(點擊,滾動,拖拽)
 
3.主線程的使用注意:
·別將比較耗時的操做放在主線程中(影響UI流暢性)
·若將耗時操做放在主線程,主線程是串行執行,用戶會感受很卡,用戶體驗會不好
 
 
二。耗時操做的執行
1.若將耗時操做放在子線程(後天線程,非主線程)
·好處:①在用戶點擊按鈕的那一刻就有反應;②能同時處理耗時操做和UI控件的事件
 
三。iOS中多線程的實現方案
1.pthread:c語言
①一套統一的多線程API
②使用與Unix、Linux、Windows等系統
③跨平臺、可移植
④使用難度比較大
⑤.程序員管理線程聲明週期
⑥.使用頻率:幾乎不用
 
2.NSThread:OC語言
①使用更加面向對象
②簡單易用,可直接操做線程對象
③。線程聲明週期:程序員管理
④。偶爾使用
 
3.GCD :c語言
①指針替代NSThread等線程技術
②重複利用設備的多核
③。線程生命週期:自動管理
④使用頻率:常用
 
4.NSOperation OC語言
①基於GCD(底層是GCD)
②比GCD多了一些更堅決的功能
③使用更加面向對象
③。線程生命週期:自動管理
④使用頻率:常用
 
-------建立和啓動線程--
1.一個NSThread對象就表明一條線程
2.建立、啓動線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector(run) object :nil];
[thread start];
// 線程一啓動,就會在線程thread中執行self的run方法
3.主線程相關用法
+ (NSThread *)mainThread; // 得到主線程
- (BOOL)isMainThread; // 是否爲主線程
+ (BOOL)isMainThread; // 是否爲主線程
-----其它用法--
 
1.得到當前線程
NSThread *current = [NSThread currentThread];
 
2.線程的調度優先級
+ ( double )threadPriority;
+ (BOOL)setThreadPriority:( double )p;
- ( double )threadPriority;
- (BOOL)setThreadPriority:( double )p;
調度優先級的取值範圍是0.0 ~ 1.0,默認0.5,值越大,優先級越高
 
3.線程的名字
- ( void )setName:(NSString *)n;
- (NSString *)name;
 
-------建立線程的3種方式----
/**
  *  NSThread建立方式3:隱世線程建立,而且直接(自動)啓動線程
  */
- ( void )threadCreate3
{
     [self performSelectorInBackground:@selector(run:) withObject: @"333333" ];
}
 
/**
  *  建立方式2:建立完線程後自動啓動線程
  */
- ( void )threadCreate2
{
     // 分離出的子線程
     [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject: @"2222222" ];
}
 
/**
  *  建立方式1:①先建立初始化子線程②再啓動
  */
- ( void )threadCreate
{
     NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object : @"heheh" ];
     thread1.name = @"thread1" ;
     // 開啓線程
     [thread1 start];
     
     NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object : @"heheh" ];
     thread2.name = @"thread2" ;
     // 開啓線程
     [thread2 start];
     
     NSThread *thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object : @"heheh" ];
     thread3.name = @"33" ;
     // 開啓線程
     [thread3 start];
}
 
----
 
·~方法2,3相對於方法1的優缺點
·優勢:簡單快捷
·肯定:沒法對線程進行更詳細的設置
 
-----------------線程的5種狀態--------
新建 就緒  運行  阻塞  死亡
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object : @"heheh" ];
代碼執行完,在內存中新建一個線程對象,它處於新建狀態不會運行,而後調用
[thread1 start];
內存中會出現一個可調度線程池,start狀態以後,新建的線程從 新建狀態變爲就緒狀態,等待cpu的調度,cpu一旦調度,就會從就緒狀態變爲運行狀態;期間若cpu調度其它線程對象,線程對象又變成就緒狀態,再調度再進入運行狀態。
若調用了sleep方法、等待同步鎖,那麼線程對象會先進入阻塞狀態,它會可調度線程池中清除
線程任務執行完畢、異常、強制退出,線程對象進入死亡狀態,線程對象也會從線程調度池中移除,可是還在內存中。
 
 
-----------控制線程狀態------
1。啓動線程
- ( void )start;
// 進入就緒狀態 - 》 運行狀態。當鹹菜任務執行完畢,自動進入死亡狀態
2.阻塞(暫停)線程
+ ( void )sleepUntilDate:(NSDate *)date;
+ ( void )sleepForTimeInterval:(NSTimeInterval)ti;
// 進入阻塞狀態
 
3.強制中止線程
+ ( void )exit;
// 進入死亡狀態,注意:一旦進入死亡狀態,線程就不能再用了
// notice:一旦線程tingzhil,就不嫩哥在此開啓任務
 
--------------線程的安全問題(多線程的安全隱患)-------
1.資源共享
·一塊資源可能會被多個線程共享,即多個線程可能會訪問同一塊資源
·好比多個線程訪問同一個對象,同一個變量、同一個文件
 
2.當多個線程訪問同一塊資源時,很容易引起數據錯亂和數據安全問題
 
3.實例
eg。1 存錢取錢
eg。2 賣票
 
-----------------安全隱患解決-互斥鎖-----
1.格式
@synchronized(鎖對象)
{ // 須要鎖定的代碼
}
注意:鎖定一份代碼只能用1把鎖,用多把鎖是無效的
2.優缺點
·優勢:能有效防止多線程搶奪資源形成的安全問題
·缺點:須要消耗大量的cpu資源
 
3.互斥鎖的使用前提:多條線程搶奪同一塊資源
 
4.相關術語:線程同步
·means:多條線程按順序的執行任務
·互斥鎖就是使用了線程同步技術
 
-----------------原子和非原子屬性----------------------
1.OC在定義屬性時有nonatomomic和atomic兩種選擇
·atomic:原子屬性,爲setter方法加鎖(默認就是atomic)
·nonatomic:非原子屬性,不會爲setter方法加鎖
2.atomic加鎖原理
@property (assign, atomic) int age;
- ( void )setAge:( int )age
{
     @synchronized(self)
     {
         _age = age;
     }
}
----------原子和非原子屬性的選擇----
1.nonatomic和atomic對比
·atomic:線程安全,須要消耗大量資源
·nonatomic:非線程安全,適合內存小的移動設備
 
2.iOS開發的建議
·全部屬性都聲明爲nonatomic
·儘可能避免多線程搶奪同一塊資源
·儘可能將加鎖、資源搶奪的業務邏輯交給服務器端處理,減少移動客戶端的壓力
 
 
-----------------線程間的通訊----------------------
1.什麼叫線程間的通訊
·在一個進程中,線程每每不是鼓勵挫折的,多個線程之間須要進行通訊
 
2.線程間通訊的體現
·一個線程傳遞數據給另外一個線程
·在一個線程中執行完特定任務後,轉到另外一個線程繼續執行任務
 
3.線程間通訊經常使用方法
- ( void )performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- ( void )performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
 
----------------GCD---------------
1.GCD 全稱:Grand Central Dispatch,可譯爲「偉大的中樞調度器」
,純c語言,提供了飛奪強大的函數
 
2.GCD的優點
·GCD是蘋果公司爲多核的並行運算提出的解決方案
·GCD會自動利用更多的CPU內核(好比雙核、四核)
·GCD會自動管理線程的生命週期(建立線程、調度任務、銷燬線程)
·程序員只須要告訴GCD想要執行什麼任務,不須要編寫任何線程管理代碼
 
------GCD的任務和隊列--
1.GCD中又2個核心概念:
·任務:執行什麼操做
·隊列:用來存聽任務
 
2.GCD的使用就2個步驟
·①定製任務:
·》肯定想作的事情
·②將任務添加到隊列中:
·》GCD會自動將隊列中的任務取出,放到對應的線程中執行
·》任務的取出遵循隊列的FIFO原則:先進先出,後進後出
 
 
----GCD執行任務-----
1.GCD中有2個用來執行任務的函數
·用同步的方式執行任務
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)
·》queue:隊列
·》block:任務
·用異步的方式執行任務
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
 
2.同步和異步的區別
·同步:在當前線程中執行
·異步:在另外一條線程中執行
 
-------GCD的隊列類型----
1.GCD的隊列能夠分爲兩大類型
·併發隊列(Concurrent Dispatch Queue)
》可讓多個任務併發(同時)執行(自動開啓多個線程同時執行任務)
》併發功能只有在異步函數(dispatch_async)中才有效
·串行隊列(Serial Dispatch Queue)
》讓任務一個接着一個的執行(一個任務執行完畢後,在執行下一個任務)
---------GCD中容易混淆的術語----
1.有四個術語比較容易混淆:同步、異步、併發、串行
·同步和異步決定了要不要開啓新的線程
·》同步:在當前線程中執行任務,不具有開啓新線程的能力
·》異步:在新的線程中執行任務,具有開啓新線程的能力
 
·併發和串行決定了任務的執行方式
·》併發:多個任務併發(同時)執行
·》串行:一個任務執行完畢後,再執行下一個任務
 
---GCD的併發隊列---
1.GCD默認已經提供了全局的併發隊列,供整個應用使用,不須要手動建立
·使用dispatch_get_global_queue函數得到全局的併發隊列
dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority, //隊列的優先級
                                            unsigned long flags; // 此參數暫時無用,用0便可
);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 得到全局併發隊列
2.全局併發隊列的優先級
·#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
·#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(中)
·#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
·#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN //後臺
 
----GCD的串行隊列----
1.GCD中得到串行有2種途徑
·使用dispatch_queue_create函數差ungjchuanx隊列
dispatch_queue_t
dispatch_queue_create( const char *label, // 隊列名稱
dispatch_queue_attr_t attr // 隊列屬性,通常用NULL便可
);
dispatch_queue_t queue = dispatch_queue_create( "cn.itcast.queue" , NULL); // 建立
dispatch_release(queue); // 非ARC須要是轟動釋放建立的隊列
 
·使用主隊列(跟主線程相關聯的隊列)
·》主隊列是GCD自帶的一種特殊的串行隊列
·》放在主隊列中的任務,都會放到主線程中執行
·》使用dispatch_get_main_queue()得到主隊列
dispatch_queue_t queue = dispatch_get_main_queue();
 
-----GCD各類隊列的執行效果----
 
/**
  *  使用dispatch_async異步函數,在主線程中網主隊列中添加任務
  */
- ( void )testAsyncMainQueue {
     // 1.得到主隊列
     dispatch_queue_t queue = dispatch_get_main_queue();
     // 2.添加任務到隊列中,執行任務
     dispatch_async(queue, ^{
         NSLog( @"---------1-----%@" , [NSThread currentThread]);
     });
     
     // 總結:不開新線程
     
}
 
/**
  *  使用dispatch_sync同步函數,在主線程中網主隊列中添加任務,死:任務沒法往下執行
  */
- ( void )testSyncMainQueue {
     // 1.得到主隊列
     dispatch_queue_t queue = dispatch_get_main_queue();
     // 2.添加任務到隊列中,執行任務
     dispatch_sync(queue, ^{
         NSLog( @"---------1-----%@" , [NSThread currentThread]);
     });
     
     
     // 總結:不開新線程,全部任務在主線程中串行執行
}
 
// 凡是函數名中帶有create、copy、new、retain等字眼,都須要在不須要使用這個數據的時候進行release
// GCD的數據類型在ARC環境下不須要再作release
// CF(Core Foundation)的數據類型在ARC環境下仍然要作release
 
- ( void )testCF {
     CFArrayRef array = CFArrayCreate(NULL, NULL, 11, NULL);
     CFRelease(array);
}
 
/**
  *  用dispatch_sync同步函數往串行隊列中添加任務
  */
- ( void )testSyncSerialQueue {
     // 1.建立串行隊列
     dispatch_queue_t queue = dispatch_queue_create( "cn.dongyue.queue" , NULL);
     
     // 2.添加任務到隊列中,執行任務
     dispatch_sync(queue, ^{
         NSLog( @"---------1-----%@" , [NSThread currentThread]);
     });
     dispatch_sync(queue, ^{
         NSLog( @"---------2-----%@" , [NSThread currentThread]);
     });
     dispatch_sync(queue, ^{
         NSLog( @"---------3-----%@" , [NSThread currentThread]);
     });
     
     // 3.釋放(MRC)
     //dispatch_release(queue);
     
     
     // 總結:不會開新的線程
}
 
/**
  *  用dispatch_sync同步函數往併發隊列中添加任務
  */
- ( void )testSyncGlobalQueue {
     // 1.得到全局的併發隊列
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     
     // 2.添加任務到隊列中,執行任務
     dispatch_sync(queue, ^{
         NSLog( @"---------1-----%@" , [NSThread currentThread]);
     });
     dispatch_sync(queue, ^{
         NSLog( @"---------2-----%@" , [NSThread currentThread]);
     });
     dispatch_sync(queue, ^{
         NSLog( @"---------3-----%@" , [NSThread currentThread]);
     });
     
     // 總結:不會開啓新的線程,併發隊列失去了併發功能
}
/**
  *  用dispatch_async同步函數往併發隊列中添加任務
  */
- ( void )testAsyncSerialQueue {
     // 1.建立串行隊列
     dispatch_queue_t queue = dispatch_queue_create( "cn.dongyue.queue" , NULL);
     
     // 2.添加任務到隊列中,執行任務
     dispatch_async(queue, ^{
         NSLog( @"---------1-----%@" , [NSThread currentThread]);
     });
     dispatch_async(queue, ^{
         NSLog( @"---------2-----%@" , [NSThread currentThread]);
     });
     dispatch_async(queue, ^{
         NSLog( @"---------3-----%@" , [NSThread currentThread]);
     });
     
     // 總結:只開1個新的線程,不會開啓新的線程
}
/**
  *  用dispatch_async同步函數往併發隊列中添加任務
  */
- ( void )testAsyncGlobalQueue {
     // 1.得到全局的併發隊列
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     
     // 2.添加任務到隊列中,執行任務
     dispatch_async(queue, ^{
         NSLog( @"---------1-----%@" , [NSThread currentThread]);
     });
     dispatch_async(queue, ^{
         NSLog( @"---------2-----%@" , [NSThread currentThread]);
     });
     dispatch_async(queue, ^{
         NSLog( @"---------3-----%@" , [NSThread currentThread]);
     });
     // 總結:同時開啓了3個線程
}
 
----------線程間通訊示例---------
1.從子線程回到主線程(下載圖片)
- ( void )testBackToMain {
     // 獲取全局併發隊列
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     // 異步隊列
     dispatch_async(queue, ^{
         NSLog( @"-----%@" , [NSThread currentThread]);
         // 下載圖片
         NSString *path = @"圖片連接的網址" ;
         NSURL *url = [NSURL URLWithString:path];
         NSData *data = [NSData dataWithContentsOfURL:url];
         
         UIImage *image = [UIImage imageWithData:data];
         // 回到主線程顯示圖片
         dispatch_async(dispatch_get_main_queue(), ^{
             NSLog( @"-----------%@" , [NSThread currentThread]);
             self.iconView.image = image;
         });
     });
}
--------GCD的延時執行----
1.iOS常見的延時執行有2種方式
·調用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒後再調用self的run方法
 
·使用GCD函數
- ( void )testDelay {
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         NSLog( @"222" );
     });
}
---------一次性代碼-----
- ( void )testOnce {
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         NSLog( @"once" );
     });
}
------------隊列組------
1.有這麼一種需求
·首先:分別異步執行2個耗時的操做
·其次:等2各異步操做都執行完畢後,再回到主線程執行操做
 
2.若想要快速高效的實現上述需求,能夠考慮用隊列組
 
- ( void )touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
     NSLog( @"%@" , [NSThread currentThread]);
     dispatch_group_t group = dispatch_group_create();
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     
     __block UIImage *icon1 = nil;
     dispatch_group_async( group , queue, ^{
         NSLog( @"%@" , [NSThread currentThread]);
         //
         icon1 = [self imageWithURL: @"http://image.cache.xiu8.com/live/125/125/997729.jpg" ];
         
     });
     __block UIImage *icon2 = nil;
     dispatch_group_async( group , queue, ^{
         NSLog( @"%@" , [NSThread currentThread]);
         //
     });
     
     dispatch_group_notify( group , dispatch_get_main_queue(), ^{
         NSLog( @"%@" , [NSThread currentThread]);
         //
         self.iconV1.image = icon1;
         self.iconV2.image = icon2;
         
         UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0);
         [icon1 drawInRect:CGRectMake(0, 0, 100, 100)];
         [icon2 drawInRect:CGRectMake(100, 0, 100, 100)];
         self.bigIconV.image = UIGraphicsGetImageFromCurrentImageContext();
         
         UIGraphicsEndImageContext();
     });
}
 
- (UIImage *)imageWithURL:(NSString *)iconPath
{
     NSLog( @"%@" , [NSThread currentThread]);
     NSURL *url = [NSURL URLWithString:iconPath];
     NSData *data = [NSData dataWithContentsOfURL:url];
     return [UIImage imageWithData:data];

}html

相關文章
相關標籤/搜索