iOS中你必須了解的多線程

多線程概念詳解

什么是進(jìn)程?
  • 簡(jiǎn)單的說進(jìn)程就是我們電腦上運(yùn)行的一個(gè)個(gè)應(yīng)用程序,每一個(gè)程序就是一個(gè)進(jìn)程,并且每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程運(yùn)行在其專用受保護(hù)的內(nèi)存空間內(nèi)(window系統(tǒng)可以通過任務(wù)管理器進(jìn)行查看,Mac系統(tǒng)中可以通過活動(dòng)監(jiān)視器對(duì)其進(jìn)行查看)
什么是線程?
  • 通過上面的介紹我們知道了什么是進(jìn)程,那么如何讓進(jìn)程運(yùn)行起來,這個(gè)時(shí)候就要有線程了,也就是說每個(gè)應(yīng)用程序想要跑起來,最少也要有一條線程存在,其實(shí)應(yīng)用程序啟動(dòng)的時(shí)候我們的系統(tǒng)就會(huì)默認(rèn)幫我們的應(yīng)用程序開啟一條線程,這條線程也叫做'主線程',或者'UI線程'
進(jìn)程和線程之間的關(guān)系
  • 線程是進(jìn)行的執(zhí)行單元,進(jìn)程的所有任務(wù)都在線程中執(zhí)行,舉例來說:進(jìn)程就好比公司中的一個(gè)個(gè)部門,線程則代表著部門中的同事,而主線程當(dāng)然是我們的老板了,一個(gè)公司部能沒有老板,一個(gè)程序不能沒有線程其實(shí)都是一個(gè)道理.
什么是CPU?
  • CPU(中央處理器,Central Processing Unit)是一塊超大規(guī)模的集成電路,只要用來解釋計(jì)算機(jī)指令以及處理計(jì)算機(jī)軟件中的數(shù)據(jù).
多線程的原理
  • 同一時(shí)間,CPU值能處理一個(gè)線程,只有一條線程在執(zhí)行,多線程指的就是多條線程同時(shí)執(zhí)行,其實(shí)就是CPU快速的在多條線程之間的切換,如果CPU調(diào)度線程的時(shí)間足夠快,那么就會(huì)造成多線程并發(fā)執(zhí)行的假象,而當(dāng)線程特別多的時(shí)候,那么CPU在多條切換的效率也就會(huì)下降,同時(shí)消耗大量的CPU資源,線程的執(zhí)行效率也就會(huì)下降.
多線程優(yōu)點(diǎn)
  • 能適當(dāng)提高程序的執(zhí)行效率
  • 能適當(dāng)提高資源的利用率
多線程的缺點(diǎn)
  • 開啟線程需要占用一定的內(nèi)存空間,如果開啟大量的線程,則會(huì)占用大量的內(nèi)存空間,降低程序的性能
  • 線程越多,CPU在調(diào)度線程上得開銷就越大,程序的設(shè)計(jì)上也就更加的復(fù)雜,因此不推薦開啟太多的線程,一般開啟2~5條為最佳(且用且珍惜);
主線程
  • 也就是應(yīng)用程序啟動(dòng)的時(shí)候,系統(tǒng)默認(rèn)幫我們創(chuàng)建的線程,稱之為'主線程'或者是'UI線程';
  • 主線程的作用一般都是用來顯示或者刷新UI界面例如:點(diǎn)擊,滾動(dòng),拖拽等事件
IOS中多線程的實(shí)現(xiàn)方案
方案 簡(jiǎn)介 語言 線程生命周期 使用頻率
pthread <ul><li>一套通用的多線程API</li><li>適用于 Unix / Linux / Windows 等系統(tǒng)</li><li>跨平臺(tái)\可移植</li><li>使用難度大</li></ul> C 程序員管理 幾乎不用
NSThread <ul><li>使用更加面向?qū)ο?lt;/li><li>簡(jiǎn)單易用猬腰,可直接操作線程對(duì)象</li></ul> OC 程序員管理 偶爾使用
GCD <ul><li>旨在替代NSThread等線程技術(shù)</li><li>充分利用設(shè)備的多核</li></ul> C 自動(dòng)管理 經(jīng)常使用
NSOperation <ul><li>基于GCD(底層是GCD)</li><li>比GCD多了一些更簡(jiǎn)單實(shí)用的功能</li><li>使用更加面向?qū)ο?lt;/li></ul> OC 自動(dòng)管理 經(jīng)常使用
NSThread使用
創(chuàng)建線程的種方式
  • 通過NSThread的對(duì)象方法
  • 通過NSThread的類方法
  • 通過NSObject
NSThread 是iOS提供的輕量級(jí)開發(fā),使用起來也相對(duì)簡(jiǎn)單
為什么要使用NSThread ?
  • 在開發(fā)中我們經(jīng)常去網(wǎng)絡(luò)上下載資源,由于網(wǎng)絡(luò)原因,有的時(shí)候我們很難保證下載很快就能完成,這時(shí)候使用NSThread將下載的過程交給線程,那么在下載的同時(shí)我們的程序并不需要等待,可以繼續(xù)操作界面,程序體驗(yàn)會(huì)更好.
NSThread-Demo:
//點(diǎn)擊下載圖片
- (IBAction)downLoadButton:(id)sender {
    //通過NSThread對(duì)象方法
    //[self objectMethod];
    //通過NSThread類方法
    //[self classMethod];
    //通過NSObject的方法
    [self extendedMethod];
}

//通過NSObject的方法去下載圖片
- (void)extendedMethod{
    //通過NSObject分類方法
   [self performSelectorInBackground:@selector(downLoadImage) withObject:nil];
   
}

//通過NSThread類方法去下載圖片
- (void)classMethod{
    //NSThread類方法
    [NSThread detachNewThreadSelector:@selector(downLoadImage) toTarget:self withObject:nil];
}

//通過NSThread對(duì)象方法去下載圖片
- (void)objectMethod{
    //創(chuàng)建一個(gè)程序去下載圖片
    NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(downLoadImage) object:nil];
    NSLog(@"downLoadButton:%@",[NSThread currentThread]);//主線程
    //開啟線程
    [thread start];
}

//下載圖片
- (void)downLoadImage{
    //請(qǐng)求圖片資源
    NSURL *url=[NSURL URLWithString:@"http://pic7.nipic.com/20100515/2001785_115623014419_2.jpg"];
    //模擬下載延遲
    [NSThread sleepForTimeInterval:10];
    //將資源轉(zhuǎn)換為二進(jìn)制
    NSData *data=[NSData dataWithContentsOfURL:url];
    NSLog(@"downLoadImage:%@",[NSThread currentThread]);//在子線程中下載圖片
    //在主線程更新UI
    [self performSelectorOnMainThread:@selector(updateImage:) withObject:data waitUntilDone:YES];
    
}

//更新imageView
- (void)updateImage:(NSData *)data{
    NSLog(@"updateImage:%@",[NSThread currentThread]);//在主線程中更新UI
    //將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換為圖片
    UIImage *image=[UIImage imageWithData:data];
    //設(shè)置image
    self.imageView.image=image;
}

以上就是對(duì)NSThread三種使用的一個(gè)簡(jiǎn)單的Demo
線程狀態(tài)
  • 新建
    • 實(shí)例化線程對(duì)象
  • 就緒
    • 向線程對(duì)象發(fā)送 start 消息颓帝,線程對(duì)象被加入 可調(diào)度線程池 等待 CPU 調(diào)度
    • detachNewThreadSelector 方法和 performSelectorInBackground 方法會(huì)直接實(shí)例化一個(gè)線程對(duì)象并加入 可調(diào)度線程池
  • 運(yùn)行
    • CPU 負(fù)責(zé)調(diào)度可調(diào)度線程池中線程的執(zhí)行
    • 線程執(zhí)行完成之前(死亡之前)扫茅,狀態(tài)可能會(huì)在就緒運(yùn)行之間來回切換
    • 就緒運(yùn)行之間的狀態(tài)變化由 CPU 負(fù)責(zé)庶喜,程序員不能干預(yù)
  • 阻塞
    • 當(dāng)滿足某個(gè)預(yù)定條件時(shí)渐溶,可以使用休眠或鎖阻塞線程執(zhí)行
      • sleepForTimeInterval:休眠指定時(shí)長(zhǎng)
      • sleepUntilDate:休眠到指定日期
      • @synchronized(self):互斥鎖
  • 死亡
    • 正常死亡
      • 線程執(zhí)行完畢
    • 非正常死亡
      • 當(dāng)滿足某個(gè)條件后伊滋,在線程內(nèi)部自己中止執(zhí)行(自殺)
      • 當(dāng)滿足某個(gè)條件后饲嗽,在主線程給其它線程打個(gè)死亡標(biāo)記(下圣旨),讓子線程自行了斷.(被逼著死亡)
線程的屬性(name)
  • 隨著我們以后開發(fā)中項(xiàng)目的日益增大,Bug也就會(huì)相對(duì)的越來越隱藏越來越難查找,這時(shí)候如果是線程問題造成的,那么我們就可以在使用的時(shí)候給每個(gè)線程起一個(gè)名字,這樣一來當(dāng)程序出問題了我們可以很快的找到問題所在的線程并處理它
線程的優(yōu)先級(jí)(threadPriority)
  • 當(dāng)多個(gè)線程同時(shí)存在的時(shí)候那么就會(huì)引出一個(gè)問題,到底哪個(gè)線程該先執(zhí)行哪個(gè)線程應(yīng)該最后執(zhí)行,因此也就有了線程的優(yōu)先級(jí),線程優(yōu)先級(jí)是用浮點(diǎn)數(shù)表示的,最高為1.0 最低為0.0 默認(rèn)我們創(chuàng)建出來的線程為0.5.
  • 修改線程優(yōu)先級(jí)我們可以提高某個(gè)線程供CPU調(diào)度的可能性,開發(fā)中很少有去修改線程優(yōu)先級(jí)的
棧區(qū)大小(stackSize)
  • 默認(rèn)情況下主線程和子線程在棧區(qū)大小都是512k
是否是主線程(isMainThread)
  • 開發(fā)中我們判斷某條線程是否是主線程其實(shí)很簡(jiǎn)單,除了通過isMainThread屬性,我們還可以直接在代碼中打印出來[NSThread currentThread],其中會(huì)記錄線程名稱name和編號(hào)number,如果number為1 則為主線程
線程間的通訊
  • 其實(shí)從上面的Demo我們不難發(fā)現(xiàn),線程間的通訊簡(jiǎn)單的來說就是把耗時(shí)的操作拿到子線程中執(zhí)行(例如下載一些網(wǎng)絡(luò)資源),最后返回主線程中更新UI.
GCD的使用
  • GCD(Grand Central Dispatch)是基于C語言開發(fā)的一套多線程開發(fā)機(jī)制,是完全面向過程的叉趣。
GCD 核心概念
  1. 任務(wù)添加到隊(duì)列,并且指定執(zhí)行任務(wù)的函數(shù)
  2. 任務(wù)使用 block 封裝
    • 任務(wù)的 block 沒有參數(shù)也沒有返回值
  3. 執(zhí)行任務(wù)的函數(shù)
    • 異步 dispatch_async
      • 不用等待當(dāng)前語句執(zhí)行完畢阵谚,就可以執(zhí)行下一條語句
      • 會(huì)開啟線程執(zhí)行 block 的任務(wù)
      • 異步是多線程的代名詞
    • 同步 dispatch_sync
      • 必須等待當(dāng)前語句執(zhí)行完畢烟具,才會(huì)執(zhí)行下一條語句
      • 不會(huì)開啟線程
      • 在當(dāng)前執(zhí)行 block 的任務(wù)
  4. 隊(duì)列 - 負(fù)責(zé)調(diào)度任務(wù)
    • 串行隊(duì)列
      • 一次只能"調(diào)度"一個(gè)任務(wù)
      • dispatch_queue_create("queue", NULL);
    • 并發(fā)隊(duì)列
      • 一次可以"調(diào)度"多個(gè)任務(wù)
      • dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    • 主隊(duì)列
      • 專門用來在主線程上調(diào)度任務(wù)的隊(duì)列
      • 不會(huì)開啟線程
      • 主線程空閑時(shí)才會(huì)調(diào)度隊(duì)列中的任務(wù)在主線程執(zhí)行
      • dispatch_get_main_queue();
階段性小結(jié)
  • 開不開線程由執(zhí)行任務(wù)的函數(shù)決定
    • 異步開朝聋,異步是多線程的代名詞
    • 同步不開
  • 開幾條線程由隊(duì)列決定
    • 串行隊(duì)列開一條線程
    • 并發(fā)隊(duì)列開多條線程,具體能開的線程數(shù)量由底層線程池決定
      • iOS 8.0 之后翼馆,GCD 能夠開啟非常多的線程
      • iOS 7.0 以及之前,GCD 通常只會(huì)開啟 5~6 條線程
- 隊(duì)列的選擇
  • 多線程的目的:將耗時(shí)的操作放在后臺(tái)執(zhí)行金度!

  • 串行隊(duì)列应媚,只開一條線程,所有任務(wù)順序執(zhí)行

    • 如果任務(wù)有先后執(zhí)行順序的要求
    • 效率低 -> 執(zhí)行慢 -> "省電"
    • 有的時(shí)候猜极,用戶其實(shí)不希望太快中姜!例如使用 3G 流量,"省錢"
  • 并發(fā)隊(duì)列跟伏,會(huì)開啟多條線程丢胚,所有任務(wù)不按照順序執(zhí)行

    • 如果任務(wù)沒有先后執(zhí)行順序的要求
    • 效率高 -> 執(zhí)行快 -> "費(fèi)電"
    • WIFI,包月
實(shí)際開發(fā)中受扳,線程數(shù)量如何決定?
  • WIFI 線程數(shù) 6
  • 3G / 4G 移動(dòng)開發(fā)的時(shí)候携龟,2~3條,再多會(huì)費(fèi)電費(fèi)錢勘高!
同步 & 異步
  • 同步:
    • 在當(dāng)前線程中執(zhí)行,必須等待當(dāng)前語句執(zhí)行完畢峡蟋,才會(huì)執(zhí)行下一條語句
    • 不在當(dāng)前線程中執(zhí)行,不用等待當(dāng)前語句執(zhí)行完畢,就可以執(zhí)行下一條語句
NSThread-Demo
//點(diǎn)擊屏幕調(diào)用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"touchesBegan開始執(zhí)行");
    //同步調(diào)用
    [self start];
    
    //異步調(diào)用
    //[self performSelectorInBackground:@selector(start) withObject:nil];
    NSLog(@"touchesBegan執(zhí)行結(jié)束");
}

//開始執(zhí)行
- (void)start{
    NSLog(@"start開始執(zhí)行");
    NSLog(@"start當(dāng)前線程:%@",[NSThread currentThread]);
    //延遲5秒調(diào)用
    [NSThread sleepForTimeInterval:5];
    NSLog(@"start執(zhí)行完畢");
}

執(zhí)行以上代碼,發(fā)現(xiàn)當(dāng)同步的時(shí)候執(zhí)行的結(jié)果為:
2015-11-26 18:17:29.621 GCD同步異步[1267:159851] touchesBegan開始執(zhí)行
2015-11-26 18:17:29.621 GCD同步異步[1267:159851] start開始執(zhí)行
2015-11-26 18:17:29.621 GCD同步異步[1267:159851] start當(dāng)前線程:<NSThread: 0x7fec12e01780>{number = 1, name = main}
2015-11-26 18:17:34.627 GCD同步異步[1267:159851] start執(zhí)行完畢
2015-11-26 18:17:34.627 GCD同步異步[1267:159851] touchesBegan執(zhí)行結(jié)束

執(zhí)行以上代碼,發(fā)現(xiàn)當(dāng)異步的時(shí)候執(zhí)行的結(jié)果為:
2015-11-26 18:23:10.846 GCD同步異步[1282:163977] touchesBegan開始執(zhí)行
2015-11-26 18:23:10.847 GCD同步異步[1282:163977] touchesBegan執(zhí)行結(jié)束
2015-11-26 18:23:10.848 GCD同步異步[1282:164254] start開始執(zhí)行
2015-11-26 18:23:10.848 GCD同步異步[1282:164254] start當(dāng)前線程:<NSThread: 0x7fa778f0f0f0>{number = 2, name = (null)}
2015-11-26 18:23:15.849 GCD同步異步[1282:164254] start執(zhí)行完畢
結(jié)論: 同步是從上到下有順序的執(zhí)行,異步則不是
GCD-Demo
//點(diǎn)擊屏幕的時(shí)候調(diào)用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    //異步
    //[self asyncMethod];
    //同步
    [self syncMethod];
    
}

//異步的時(shí)候調(diào)用
- (void)asyncMethod{
    //獲取全局隊(duì)列
    dispatch_queue_t queue=dispatch_get_global_queue(0, 0);
    //創(chuàng)建任務(wù)
    void(^task)()=^{
        NSLog(@"asyncMethod:%@",[NSThread currentThread]);
    };
    //執(zhí)行任務(wù)
    dispatch_async(queue, task);
    
    NSLog(@"end");
}

//同步的時(shí)候調(diào)用
- (void)syncMethod{
    //獲取全局隊(duì)列
    dispatch_queue_t queue=dispatch_get_global_queue(0, 0);
    //創(chuàng)建任務(wù)
    void(^task)()=^{
        NSLog(@"syncMethod:%@",[NSThread currentThread]);
    };
    //執(zhí)行任務(wù)
    dispatch_sync(queue, task);
    
    NSLog(@"end");
}

執(zhí)行以上代碼,發(fā)現(xiàn)當(dāng)同步的時(shí)候執(zhí)行的結(jié)果為:
2015-11-26 18:40:55.419 GCD中的同步和異步[1361:177648] syncMethod:<NSThread: 0x7fa7d14036b0>{number = 1, name = main}
2015-11-26 18:40:55.419 GCD中的同步和異步[1361:177648] end
2015-11-26 18:40:55.667 GCD中的同步和異步[1361:177648] syncMethod:<NSThread: 0x7fa7d14036b0>{number = 1, name = main}
2015-11-26 18:40:55.667 GCD中的同步和異步[1361:177648] end
2015-11-26 18:40:55.926 GCD中的同步和異步[1361:177648] syncMethod:<NSThread: 0x7fa7d14036b0>{number = 1, name = main}
2015-11-26 18:40:55.927 GCD中的同步和異步[1361:177648] end

執(zhí)行以上代碼,發(fā)現(xiàn)當(dāng)異步的時(shí)候執(zhí)行的結(jié)果為:
2015-11-26 18:41:36.351 GCD中的同步和異步[1370:178492] asyncMethod:<NSThread: 0x7fc941e02a70>{number = 2, name = (null)}
2015-11-26 18:41:36.351 GCD中的同步和異步[1370:178425] end
2015-11-26 18:41:36.576 GCD中的同步和異步[1370:178425] end
2015-11-26 18:41:36.576 GCD中的同步和異步[1370:178492] asyncMethod:<NSThread: 0x7fc941e02a70>{number = 2, name = (null)}
2015-11-26 18:41:36.810 GCD中的同步和異步[1370:178425] end
2015-11-26 18:41:36.810 GCD中的同步和異步[1370:178492] asyncMethod:<NSThread: 0x7fc941e02a70>{number = 2, name = (null)}

和NSThread對(duì)比可以發(fā)現(xiàn)
  • 所有的代碼寫在一起的华望,讓代碼更加簡(jiǎn)單蕊蝗,易于閱讀和維護(hù)
    • NSThread 通過 @selector 指定要執(zhí)行的方法,代碼分散
    • GCD 通過 block 指定要執(zhí)行的代碼赖舟,代碼集中
  • 使用 GCD 不需要管理線程的創(chuàng)建/銷毀/復(fù)用的過程蓬戚!程序員不用關(guān)心線程的生命周期
  • 如果要開多個(gè)線程 NSThread 必須實(shí)例化多個(gè)線程對(duì)象
  • NSThread 靠 NSObject 的分類方法實(shí)現(xiàn)的線程間通訊,GCD 靠 block
使用GCD下載圖片
//點(diǎn)擊下載圖片
- (IBAction)downLoadButton:(id)sender {
    //獲取全局隊(duì)列
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       //異步下載圖片
        NSURL *url=[NSURL URLWithString:@"http://pic7.nipic.com/20100515/2001785_115623014419_2.jpg"];
        //將資源轉(zhuǎn)換為二進(jìn)制
        NSData *data=[NSData dataWithContentsOfURL:url];
        //將二進(jìn)制轉(zhuǎn)化為圖片
        UIImage *image=[UIImage imageWithData:data];

        //獲取主隊(duì)列,更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            //給圖片控件賦值
            self.imageView.image=image;
        });
    });
}

串行隊(duì)列
串行隊(duì)列特點(diǎn):
  • 以先進(jìn)先出的方式,順序調(diào)度隊(duì)列中的任務(wù)執(zhí)行
  • 無論隊(duì)列中指定的任務(wù)函數(shù)是同步還是異步,都會(huì)等待前一個(gè)任務(wù)執(zhí)行完畢以后,再調(diào)度后面的任務(wù)
隊(duì)列創(chuàng)建
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);

dispatch_queue_t queue = dispatch_queue_create("queue", NULL);
串行隊(duì)列同步&異步Demo
//點(diǎn)擊屏幕的時(shí)候調(diào)用

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //同步執(zhí)行
    //[self syncMethod];
    //異步執(zhí)行
    [self asyncMethod];
}
//同步
- (void)syncMethod{
    //創(chuàng)建隊(duì)列
    dispatch_queue_t queue=dispatch_queue_create("同步", DISPATCH_QUEUE_SERIAL);
    //執(zhí)行任務(wù)
    
    for (int i=0; i<6; i++) {
        NSLog(@"1--->%d",i);
        
        dispatch_sync(queue, ^{
            NSLog(@"2--->%d---%@",i,[NSThread currentThread]
                  );
        });
    }
    NSLog(@"end");
}
//異步
- (void)asyncMethod{
    //創(chuàng)建隊(duì)列
    dispatch_queue_t queue=dispatch_queue_create("異步", DISPATCH_QUEUE_SERIAL);
    //執(zhí)行任務(wù)
    
    for (int i=0; i<6; i++) {
        NSLog(@"1--->%d",i);
        
        dispatch_async(queue, ^{
            NSLog(@"2--->%d---%@",i,[NSThread currentThread]
                  );
        });
    }
    NSLog(@"end");
}

串行隊(duì)列 同步執(zhí)行結(jié)果:
2015-11-26 19:12:20.876 串行隊(duì)列的同步和異步[1520:204175] 1--->0
2015-11-26 19:12:20.877 串行隊(duì)列的同步和異步[1520:204175] 2--->0---<NSThread: 0x7fe952c05820>{number = 1, name = main}
2015-11-26 19:12:20.877 串行隊(duì)列的同步和異步[1520:204175] 1--->1
2015-11-26 19:12:20.877 串行隊(duì)列的同步和異步[1520:204175] 2--->1---<NSThread: 0x7fe952c05820>{number = 1, name = main}
2015-11-26 19:12:20.877 串行隊(duì)列的同步和異步[1520:204175] 1--->2
2015-11-26 19:12:20.877 串行隊(duì)列的同步和異步[1520:204175] 2--->2---<NSThread: 0x7fe952c05820>{number = 1, name = main}
2015-11-26 19:12:20.878 串行隊(duì)列的同步和異步[1520:204175] 1--->3
2015-11-26 19:12:20.878 串行隊(duì)列的同步和異步[1520:204175] 2--->3---<NSThread: 0x7fe952c05820>{number = 1, name = main}
2015-11-26 19:12:20.878 串行隊(duì)列的同步和異步[1520:204175] 1--->4
2015-11-26 19:12:20.878 串行隊(duì)列的同步和異步[1520:204175] 2--->4---<NSThread: 0x7fe952c05820>{number = 1, name = main}
2015-11-26 19:12:20.878 串行隊(duì)列的同步和異步[1520:204175] 1--->5
2015-11-26 19:12:20.878 串行隊(duì)列的同步和異步[1520:204175] 2--->5---<NSThread: 0x7fe952c05820>{number = 1, name = main}
2015-11-26 19:12:20.878 串行隊(duì)列的同步和異步[1520:204175] end

串行隊(duì)列 異步執(zhí)行結(jié)果:
2015-11-26 19:12:41.923 串行隊(duì)列的同步和異步[1529:204751] 1--->0
2015-11-26 19:12:41.924 串行隊(duì)列的同步和異步[1529:204751] 1--->1
2015-11-26 19:12:41.924 串行隊(duì)列的同步和異步[1529:204829] 2--->0---<NSThread: 0x7fc99970bbe0>{number = 2, name = (null)}
2015-11-26 19:12:41.924 串行隊(duì)列的同步和異步[1529:204751] 1--->2
2015-11-26 19:12:41.924 串行隊(duì)列的同步和異步[1529:204829] 2--->1---<NSThread: 0x7fc99970bbe0>{number = 2, name = (null)}
2015-11-26 19:12:41.924 串行隊(duì)列的同步和異步[1529:204751] 1--->3
2015-11-26 19:12:41.924 串行隊(duì)列的同步和異步[1529:204829] 2--->2---<NSThread: 0x7fc99970bbe0>{number = 2, name = (null)}
2015-11-26 19:12:41.925 串行隊(duì)列的同步和異步[1529:204751] 1--->4
2015-11-26 19:12:41.925 串行隊(duì)列的同步和異步[1529:204829] 2--->3---<NSThread: 0x7fc99970bbe0>{number = 2, name = (null)}
2015-11-26 19:12:41.925 串行隊(duì)列的同步和異步[1529:204751] 1--->5
2015-11-26 19:12:41.925 串行隊(duì)列的同步和異步[1529:204829] 2--->4---<NSThread: 0x7fc99970bbe0>{number = 2, name = (null)}
2015-11-26 19:12:41.925 串行隊(duì)列的同步和異步[1529:204751] end
2015-11-26 19:12:41.925 串行隊(duì)列的同步和異步[1529:204829] 2--->5---<NSThread: 0x7fc99970bbe0>{number = 2, name = (null)}

并發(fā)隊(duì)列
并發(fā)隊(duì)列特點(diǎn):
  • 有多個(gè)線程宾抓,操作進(jìn)來之后它會(huì)將這些隊(duì)列安排在可用的處理器上,同時(shí)保證先進(jìn)來的任務(wù)優(yōu)先處理嘿棘。
  • 以先進(jìn)先出的方式旭绒,并發(fā)調(diào)度隊(duì)列中的任務(wù)執(zhí)行
  • 如果當(dāng)前調(diào)度的任務(wù)是同步執(zhí)行的挥吵,會(huì)等待任務(wù)執(zhí)行完成后忽匈,再調(diào)度后續(xù)的任務(wù)
  • 如果當(dāng)前調(diào)度的任務(wù)是異步執(zhí)行的丹允,同時(shí)底層線程池有可用的線程資源雕蔽,會(huì)再新的線程調(diào)度后續(xù)任務(wù)的執(zhí)行
隊(duì)列創(chuàng)建
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);

并行隊(duì)列同步&異步Demo
//點(diǎn)擊屏幕的時(shí)候調(diào)用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //同步
    //[self syncMethod];
    //異步
    [self asyncMethod];
}

//異步
- (void)asyncMethod{

    //并發(fā)隊(duì)列
    dispatch_queue_t queue=dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    //創(chuàng)建任務(wù)
    for(int i=0; i<6 ;i++ ){
        dispatch_async(queue, ^{
            NSLog(@"async--->%d---%@",i,[NSThread currentThread]);
        });
    }
    NSLog(@"end");
}

//同步
- (void)syncMethod{

    //并發(fā)隊(duì)列
    dispatch_queue_t queue=dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    //創(chuàng)建任務(wù)
    for(int i=0; i<6 ;i++ ){
        dispatch_sync(queue, ^{
            NSLog(@"sync--->%d---%@",i,[NSThread currentThread]);
        });
    }
    NSLog(@"end");
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

并行隊(duì)列 同步執(zhí)行結(jié)果:
2015-11-26 21:17:19.820 并發(fā)隊(duì)列的同步與異步[1632:233685] sync--->0---<NSThread: 0x7fd762400f70>{number = 1, name = main}
2015-11-26 21:17:19.820 并發(fā)隊(duì)列的同步與異步[1632:233685] sync--->1---<NSThread: 0x7fd762400f70>{number = 1, name = main}
2015-11-26 21:17:19.821 并發(fā)隊(duì)列的同步與異步[1632:233685] sync--->2---<NSThread: 0x7fd762400f70>{number = 1, name = main}
2015-11-26 21:17:19.821 并發(fā)隊(duì)列的同步與異步[1632:233685] sync--->3---<NSThread: 0x7fd762400f70>{number = 1, name = main}
2015-11-26 21:17:19.821 并發(fā)隊(duì)列的同步與異步[1632:233685] sync--->4---<NSThread: 0x7fd762400f70>{number = 1, name = main}
2015-11-26 21:17:19.821 并發(fā)隊(duì)列的同步與異步[1632:233685] sync--->5---<NSThread: 0x7fd762400f70>{number = 1, name = main}
2015-11-26 21:17:19.822 并發(fā)隊(duì)列的同步與異步[1632:233685] end

并行隊(duì)列 異步執(zhí)行結(jié)果:
2015-11-26 21:17:45.000 并發(fā)隊(duì)列的同步與異步[1641:234351] end
2015-11-26 21:17:45.000 并發(fā)隊(duì)列的同步與異步[1641:234532] async--->3---<NSThread: 0x7fa823c9a910>{number = 5, name = (null)}
2015-11-26 21:17:45.000 并發(fā)隊(duì)列的同步與異步[1641:234425] async--->0---<NSThread: 0x7fa823f0e720>{number = 3, name = (null)}
2015-11-26 21:17:45.000 并發(fā)隊(duì)列的同步與異步[1641:234423] async--->2---<NSThread: 0x7fa823e079f0>{number = 4, name = (null)}
2015-11-26 21:17:45.000 并發(fā)隊(duì)列的同步與異步[1641:234533] async--->4---<NSThread: 0x7fa823e000b0>{number = 6, name = (null)}
2015-11-26 21:17:45.000 并發(fā)隊(duì)列的同步與異步[1641:234424] async--->1---<NSThread: 0x7fa823d4d4e0>{number = 2, name = (null)}
2015-11-26 21:17:45.001 并發(fā)隊(duì)列的同步與異步[1641:234534] async--->5---<NSThread: 0x7fa823f0a290>{number = 7, name = (null)}

全局隊(duì)列
  • 是系統(tǒng)為了方便程序員開發(fā)提供的嚣艇,其工作表現(xiàn)與并發(fā)隊(duì)列一致
主隊(duì)列
  • 專門用來在主線程上調(diào)度任務(wù)的隊(duì)列
  • 不會(huì)開啟線程
  • 以先進(jìn)先出的方式食零,在主線程空閑時(shí)才會(huì)調(diào)度隊(duì)列中的任務(wù)在主線程執(zhí)行
  • 如果當(dāng)前主線程正在有任務(wù)執(zhí)行贰谣,那么無論主隊(duì)列中當(dāng)前被添加了什么任務(wù),都不會(huì)被調(diào)度
//點(diǎn)擊屏幕的時(shí)候調(diào)用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //全局隊(duì)列同步執(zhí)行
    [self global_queue_sync];
    //全局隊(duì)列異步執(zhí)行
    //[self global_queue_async];
    //主隊(duì)列同步執(zhí)行
    //[self main_queue_sync];
    //主隊(duì)列異步執(zhí)行
    //[self main_queue_async];
}
//主隊(duì)列同步執(zhí)行
- (void)main_queue_sync{
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    for (int i = 0; i < 10; ++i) {
        dispatch_sync(queue, ^{
            NSLog(@"main_queue_sync%@---->%d", [NSThread currentThread], i);
        });
    }
    NSLog(@"end");
}

//主隊(duì)列異步執(zhí)行
- (void)main_queue_async{
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    for (int i = 0; i < 10; ++i) {
        dispatch_async(queue, ^{
            NSLog(@"main_queue_async%@---->%d", [NSThread currentThread], i);
        });
    }
    NSLog(@"end");
}

//全局隊(duì)列異步執(zhí)行
- (void)global_queue_async{
    //獲取全局隊(duì)列
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    //執(zhí)行任務(wù)
    for (int i = 0; i < 10; ++i) {
        dispatch_async(q, ^{
            NSLog(@"global_queue_async%@---->%d", [NSThread currentThread], i);
        });
    }
    NSLog(@"end");
}

//全局隊(duì)列同步執(zhí)行
- (void)global_queue_sync{
    //獲取全局隊(duì)列
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    //執(zhí)行任務(wù)
    for (int i = 0; i < 10; ++i) {
        dispatch_sync(q, ^{
            NSLog(@"global_queue_sync%@---->%d", [NSThread currentThread], i);
        });
    }
    NSLog(@"end");
}

全局隊(duì)列 同步執(zhí)行結(jié)果:
2015-11-26 22:00:12.568 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->0
2015-11-26 22:00:12.569 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->1
2015-11-26 22:00:12.569 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->2
2015-11-26 22:00:12.569 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->3
2015-11-26 22:00:12.569 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->4
2015-11-26 22:00:12.569 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->5
2015-11-26 22:00:12.569 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->6
2015-11-26 22:00:12.569 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->7
2015-11-26 22:00:12.569 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->8
2015-11-26 22:00:12.569 全局隊(duì)列與主隊(duì)列[1895:266991] global_queue_sync<NSThread: 0x7fb871d069e0>{number = 1, name = main}---->9
2015-11-26 22:00:12.570 全局隊(duì)列與主隊(duì)列[1895:266991] end

全局隊(duì)列 異步執(zhí)行結(jié)果:
2015-11-26 22:01:01.565 全局隊(duì)列與主隊(duì)列[1908:268194] end
2015-11-26 22:01:01.566 全局隊(duì)列與主隊(duì)列[1908:268233] global_queue_async<NSThread: 0x7f97fb607e80>{number = 2, name = (null)}---->1
2015-11-26 22:01:01.566 全局隊(duì)列與主隊(duì)列[1908:268234] global_queue_async<NSThread: 0x7f97fb61f710>{number = 4, name = (null)}---->2
2015-11-26 22:01:01.566 全局隊(duì)列與主隊(duì)列[1908:268256] global_queue_async<NSThread: 0x7f97fb63bec0>{number = 5, name = (null)}---->3
2015-11-26 22:01:01.566 全局隊(duì)列與主隊(duì)列[1908:268257] global_queue_async<NSThread: 0x7f97fb7127d0>{number = 6, name = (null)}---->4
2015-11-26 22:01:01.566 全局隊(duì)列與主隊(duì)列[1908:268232] global_queue_async<NSThread: 0x7f97fb70bde0>{number = 3, name = (null)}---->0
2015-11-26 22:01:01.566 全局隊(duì)列與主隊(duì)列[1908:268233] global_queue_async<NSThread: 0x7f97fb607e80>{number = 2, name = (null)}---->5
2015-11-26 22:01:01.566 全局隊(duì)列與主隊(duì)列[1908:268234] global_queue_async<NSThread: 0x7f97fb61f710>{number = 4, name = (null)}---->6
2015-11-26 22:01:01.566 全局隊(duì)列與主隊(duì)列[1908:268258] global_queue_async<NSThread: 0x7f97fb442510>{number = 7, name = (null)}---->7
2015-11-26 22:01:01.566 全局隊(duì)列與主隊(duì)列[1908:268256] global_queue_async<NSThread: 0x7f97fb63bec0>{number = 5, name = (null)}---->8
2015-11-26 22:01:01.567 全局隊(duì)列與主隊(duì)列[1908:268257] global_queue_async<NSThread: 0x7f97fb7127d0>{number = 6, name = (null)}---->9

主隊(duì)列 同步執(zhí)行結(jié)果:
主隊(duì)列和主線程相互等待會(huì)造成死鎖,程序直接卡死
主隊(duì)列 異步執(zhí)行結(jié)果:
2015-11-26 22:02:09.057 全局隊(duì)列與主隊(duì)列[1935:269576] end
2015-11-26 22:02:09.058 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->0
2015-11-26 22:02:09.058 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->1
2015-11-26 22:02:09.058 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->2
2015-11-26 22:02:09.058 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->3
2015-11-26 22:02:09.058 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->4
2015-11-26 22:02:09.059 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->5
2015-11-26 22:02:09.059 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->6
2015-11-26 22:02:09.059 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->7
2015-11-26 22:02:09.059 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->8
2015-11-26 22:02:09.087 全局隊(duì)列與主隊(duì)列[1935:269576] main_queue_async<NSThread: 0x7fadd850a320>{number = 1, name = main}---->9

NSOperation
  • NSOperation有兩個(gè)常用子類用于創(chuàng)建線程操作:NSInvocationOperation和NSBlockOperation因痛,兩種方式本質(zhì)沒有區(qū)別鸵膏,但是是后者使用Block形式進(jìn)行代碼組織谭企,使用相對(duì)方便。
NSOperation-Demo
//當(dāng)點(diǎn)擊屏幕的時(shí)候調(diào)用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //創(chuàng)建url
    NSURL *url=[NSURL URLWithString:@"http://pic7.nipic.com/20100515/2001785_115623014419_2.jpg"];
    //將資源轉(zhuǎn)換為二進(jìn)制
    NSData *data=[NSData dataWithContentsOfURL:url];
    //創(chuàng)建operation
    NSInvocationOperation *operation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downLoadImage:) object:data];
    //執(zhí)行操作
    [operation start];
}

- (void)downLoadImage:(NSData *)data{
    //將二進(jìn)制轉(zhuǎn)化為圖片
    UIImage *image=[UIImage imageWithData:data];
    //顯示圖片
    self.imageView.image=image;
    
}

將任務(wù)添加到隊(duì)里中執(zhí)行
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    //創(chuàng)建隊(duì)列
    NSOperationQueue *operationQueue=[[NSOperationQueue alloc]init];
    //創(chuàng)建任務(wù)
    NSInvocationOperation *operation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downLoadImage) object:nil];
        NSLog(@"1--->%@",[NSThread currentThread]);
    //將任務(wù)添加到隊(duì)列中
    [operationQueue addOperation:operation];
}

//子線程中下載圖片
- (void)downLoadImage{
    //創(chuàng)建url
    NSURL *url=[NSURL URLWithString:@"http://pic7.nipic.com/20100515/2001785_115623014419_2.jpg"];
    //將資源轉(zhuǎn)換為二進(jìn)制
    NSData *data=[NSData dataWithContentsOfURL:url];
    NSLog(@"2--->%@",[NSThread currentThread]);
    //將二進(jìn)制轉(zhuǎn)化為tup
    UIImage *image=[UIImage imageWithData:data];
    [self performSelectorOnMainThread:@selector(updateImage:) withObject:image waitUntilDone:YES];
}

//主線程中更新圖片
- (void)updateImage:(UIImage *)image{
     NSLog(@"3--->%@",[NSThread currentThread]);
    self.imageView.image=image;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

總結(jié):無論使用何種方式進(jìn)行多線程開發(fā),都應(yīng)該遵循一定的規(guī)則(盡可能將比較耗時(shí)比如去網(wǎng)絡(luò)下載資源的操作放到子線程中執(zhí)行),注重程序的執(zhí)行效率以及用戶的體驗(yàn)合理的使用多線程(線程雖好,可不要亂用噢).

以上Demo下載路徑:https://github.com/chengaojian**

以上就是多線程中常用的一些介紹,由于時(shí)間比較倉促,很多例子都是現(xiàn)寫,如果代碼中存在錯(cuò)誤,還請(qǐng)指出,謝謝.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市俄占,隨后出現(xiàn)的幾起案子缸榄,更是在濱河造成了極大的恐慌碰凶,老刑警劉巖欲低,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砾莱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡块蚌,警方通過查閱死者的電腦和手機(jī)峭范,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門辆毡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舶掖,“玉大人眨攘,你說我怎么就攤上這事鲫售」昊ⅲ” “怎么了鲤妥?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贡耽。 經(jīng)常有香客問我蒲赂,道長(zhǎng)滥嘴,這世上最難降的妖魔是什么若皱? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任走触,我火速辦了婚禮互广,結(jié)果婚禮上兜辞,老公的妹妹穿的比我還像新娘。我一直安慰自己凶硅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布氢妈。 她就那樣靜靜地躺著首量,像睡著了一般加缘。 火紅的嫁衣襯著肌膚如雪拣宏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天辑莫,我揣著相機(jī)與錄音摆昧,去河邊找鬼绅你。 笑死忌锯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脚猾,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蛛芥!你這毒婦竟也來了仅淑?” 一聲冷哼從身側(cè)響起赡鲜,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤银酬,失蹤者是張志新(化名)和其女友劉穎哮内,沒想到半個(gè)月后纹因,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞭恰,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缰盏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片口猜。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡川抡,死狀恐怖猖腕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恨闪,我是刑警寧澤倘感,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站咙咽,受9級(jí)特大地震影響老玛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钧敞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一蜡豹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镜廉,春花似錦寂玲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扣典。三九已至笛粘,卻和暖如春示括,著一層夾襖步出監(jiān)牢的瞬間丁稀,已是汗流浹背授账。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容