01-網絡多線程基礎

1.網絡多線程基礎

1.1 學習多線程的目的

學習多線程最主要的目的是將耗時的操作放在后臺處理,保證UI界面的正常顯示與交互.

提示 : 網絡操作是非常非常耗時的.在做網絡開發時,所有網絡訪問都是耗時操作.需要在后臺線程中執行.
多線程開發的原則是 : 越簡單越好

1.2 模擬耗時操作

空的for循環不耗時
操作內存的棧區速度很快;棧區存儲空間地址是連續的;
操作內存的常量區速度很快;內存空間只開辟一次;
操作內存的堆區速度相對棧區和常量區要慢些;堆區內存空間不連續,需要尋址;
I/O操作是很耗時的; (把數據從內存輸出到外接設備,或者由外接設備輸入到內存)

  • 耗時操作對UI交互的影響 : 卡死了主屏幕,直到耗時操作執行完,屏幕的交互才能正常進行;
  • 解決耗時操作卡頓UI的辦法 : 多線程技術;
  • 學習多線程的目的 : 把耗時操作放在后臺執行,不讓耗時操作卡頓UI;

1.3.多線程基本概念

1.3.1 同步和異步

同步和異步是任務 / 代碼 執行的兩種方式

  • 同步

    • 多個任務按順序依次執行,就是同步執行
  • 異步

    • 多個任務同時執行,就是異步執行
    • 如何保證多個任務同時執行?
    • 開線程,開多個線程,就可以保證多個任務同時執行
    • 提示 : 凡是遇到異步 / 多線程 / 耗時操作 第一反應就是需要開啟新的子線程
    • 學習多線程就是為了如何讓任務在子線程異步執行

1.3.2 進程和線程

  • 進程

    • 系統中正在運行的應用程序叫做進程
    • 進程可以類必成公司
  • 線程 / 多線程

  • 程序一啟動就會默認開啟一個線程,稱之為主線程

  • 線程是進程最基本的執行單元,進程里面所有的任務都在線程中執行的

  • 一個進程,可以開啟多個線程,稱之為多線程

1.3.3 多線程執行原理

  • CPU在多個線程之間,快速來回的切換,調度線程執行任務,如果切換的速度足夠快,就造成多個任務同時執行的假象

1.3.4 多線程優缺點

  • 優點

    • 可以適當提高程序執行的效率 (開啟多個線程下載視頻)
    • 適當提高CPU和內存的利用率.
    • 線程上的任務執行完成后,線程會自動銷毀,節省內存.
  • 缺點

    • 前提 : 當線程非常多的時候,就暴露缺點
    • 會消耗大量的CPU資源
    • 時間開銷 / 空間開銷
    • 多線程的使用原則 : 能不用就不用,如果非要用,就簡單的使用,少用

1.3.5 主線程

  • 作用 : 刷新UI / 處理UI事件
    • 處理UI事件 : 點擊、滾動、拖拽等事件
  • 使用時的注意點 :
    • 不要把耗時操作放到主線程中執行
    • 耗時操作會卡住主線程,嚴重影響UI的流暢度,給用戶一種卡的壞體驗,影響UI交互質量.

1.4多線程的實現方案

POSIX 表示可移植操作系統接口(Portable Operating System Interface ) —— pthread

2. pthread

  • 學習pthread的目的 : 就是為了復習C語言相關的知識點
  • 在C語言中,一般帶_t / _ref標識數據類型
  • NULL : 表示空地址,一般在C語言使用;
  • nil : 表示空對象,一般在OC使用;
  • 其實,NULLh和nil本質上沒有半點兒區別
  • void *(*)(void *) : 表示指向函數的指針,即函數名;函數名就是表示函數地址;
  • 數組地址就是數組名或者數組第0個角標元素的地址
  • void * 表示可以指向任何地址的指針,代表任意數據類型;類似于OC的id;
返回值    函數名    函數參數
void *    (*)    (void *)

2.1 橋接

  • 使用場景 : 在C語言和OC語言混合開發時,需要做數據類型轉換,有時候需要使用橋接;
  • 橋接作用 : 在做數據類型轉換時,告訴編譯器如何管理C語言的內存
  • 提示 : 在ARC環境下,編譯器在編譯時,不會自動管理C語言申請的內存空間
  • 提示: 在ARC環境下,加上__bridge 表示告訴編譯器C語言申請的內存也是自動管理的,因為大環境是ARC的
  • 提問 : MRC環境下,需要使用__bridge 嗎? 不需要,因為本來就是手動管理的

3.NSThread

3.1 NSThread創建線程三種方式

3.1.1 構造方法

  • 可以拿到線程對象
  • 需要自己啟動線程
// 創建線程對象
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@"alloc"];
// 啟動線程
[thread start];

3.1.2 類方法

  • 不可以拿到線程對象
  • 不需要自己啟動線程
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"detach"];

3.1.3 NSObject分類方法

  • 不可以拿到線程對象
  • 不需要自己啟動線程
[self performSelectorInBackground:@selector(demo:) withObject:@"perform"];

3.2 target和selector的關系

  • 執行哪個對象的哪個方法
  • 需求 : 執行Person對象的run方法,run方法需要在子線程執行
// 創建線程對象
NSThread *thread = [[NSThread alloc] initWithTarget:_p selector:@selector(run:) object:@"person"];
// 啟動線程
[thread start];

3.3 線程生命周期 / 線程狀態

  • 新建狀態
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
  • 就緒狀態
[thread start];
  • 運行狀態 : 程序員無法干預
  • 阻塞狀態 : 調用sleep方法 / 添加互斥鎖(同步鎖)
  • 死亡狀態
    • 正常死亡 : 任務執行結束
    • 異常死亡 : exit

3.4 線程屬性

  • name : 標識唯一的線程對象,方便定位線程對象
  • threadPriority : 決定了線程有更多的機會被CPU調度執行;等同于qualityOfService;實際開發中千萬不要隨意修改
  • stackSize : 線程對象占用內存空間大小.主線程 / 子線程 512KB

3.5 多線程訪問共享資源 (會造成線程安全問題)

  • 當多個線程同時操作共享資源,就會出現線程安全問題
  • 解決辦法 : 加鎖 (互斥鎖 / 同步鎖)
  • 互斥鎖 / 同步鎖 : 使用了線程同步技術
  • 特點 : 可以保證被鎖定的代碼,同一時間只有一個線程可以訪問
  • self : 表示互斥鎖的參數;互斥鎖的參數,又叫做鎖對象;
  • 鎖對象 : 任何繼承自NSObject的對象,都可以作為互斥鎖的參數;內部有把鎖,默認是開啟的
  • 鎖對象必須是全局的對象;self是最方便獲取的全局的鎖對象
  • 局部鎖對象是鎖不住的,因為每次線程進來之前會新建一把鎖
  • 提示 : 加鎖的事情,不是再客戶端操作的;是服務器加鎖的,多線程資源共享絕大多數是在服務器發生;
  • 提示 : 加鎖是犧牲了性能,保證安全.客戶端的性能不能輕易犧牲

3.6 異步下載網絡圖片

  • 在 iOS 開發中,使用多線程只有一個目的:將耗時操作放在后臺工作,待工作完成后,通知主線程更新 UI
  • 在視圖加載前設置根視圖為scrollView
- (void)loadView {
    
    //將根視圖換為scrollview
    self.scrollView = [[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    self.view = self.scrollView;
    
    self.scrollView.backgroundColor = [UIColor blueColor];
    
    //添加圖片容器
    UIImageView *imageView = [[UIImageView alloc] init];
    
    [self.view addSubview: imageView];
    
    self.imageView = imageView;
}
  • 耗時的下載操作放在子線程
- (void)viewDidLoad {
    [super viewDidLoad];
    
//    [self loadImageData];
    
    // 在子線程執行耗時操作
    [self performSelectorInBackground:@selector(loadImageData) withObject:nil];
}
// 下載圖片的主方法
- (void)loadImageData
{
    // URL
    NSURL *URL = [NSURL URLWithString:@"http://pic.sc.chinaz.com/files/pic/pic9/201508/apic14052.jpg"];
    // 發送網絡請求,獲取圖片二進制數據,是個耗時操作
    NSData *data = [NSData dataWithContentsOfURL:URL];
    // image : 就是子線程執行的結果,需要傳遞到主線程
    UIImage *image = [UIImage imageWithData:data];
    
    // 下載完成之后,通知主線程刷新UI
    // waitUntilDone : 是否等待updateUI執行完,再執行后面的代碼,一般傳入NO
    [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:NO];
    
    NSLog(@"后面的代碼");
}
  • 更新UI的操作在主線程
// 回到主線程更新UI
- (void)updateUI:(UIImage *)image
{
    self.imgView.image = image;
    [self.imgView sizeToFit];
    self.scrollView.contentSize = image.size;
}
  • 在子線程下載圖片,在主線程更新UI,是線程間通信的一種;
  • 線程間通信 : 一個線程把他執行的結果,傳遞到另外的一個線程
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Object C中創建線程的方法是什么?如果在主線程中執行代碼,方法是什么?如果想延時執行代碼、方法又是什么? 1...
    AlanGe閱讀 1,777評論 0 17
  • 一、多線程基礎 基本概念 進程進程是指在系統中正在運行的一個應用程序每個進程之間是獨立的,每個進程均運行在其專用且...
    AlanGe閱讀 557評論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,132評論 25 708
  • 含笑努吞無根水,瀟灑怒斬萬纏念。 滴水成漣激荷霧,有動無終水自潺。
    善良的老虎閱讀 184評論 0 0
  • Pragma mark — NO.1 Pragma mark — NO.2 更新gem Pragma mark ...
    浮橋小麥閱讀 170評論 0 1