初學(xué)iOS----多線程(一:NSThread父泳、pathread般哼、NSOperation吴汪、線程安全)

![多線程.2.png](http://upload-images.jianshu.io/upload_images/1803560-898b71ebec4c2912.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

在說(shuō)多線程之前,首先要明白線程蒸眠,進(jìn)程

線程和進(jìn)程

進(jìn)程:
  • 系統(tǒng)中正在運(yùn)行的程序漾橙,稱為一個(gè)進(jìn)程
  • 每個(gè)進(jìn)程,都在自己獨(dú)立的內(nèi)存空間運(yùn)行楞卡,并且進(jìn)程之間互不影響霜运。
線程:
  • 進(jìn)程的所有任務(wù),都是在線程執(zhí)行的蒋腮,可以說(shuō)它是進(jìn)程的基本執(zhí)行單元淘捡,是程序中獨(dú)立運(yùn)行的代碼段。
  • 主線程:每個(gè)正在運(yùn)行的程序(即進(jìn)程)池摧,至少包含一個(gè)線程嘱能,這個(gè)線程叫做主線程
  • 單線程:只有一個(gè)主線程的程序翘紊,稱作單線程程序

多線程

顧名思義扇单,多線程就是擁有多個(gè)線程的程序色建,iOS用戶可以根據(jù)需要在一段進(jìn)程中開(kāi)辟新的線程誉碴,這些線程相對(duì)于主線程來(lái)說(shuō)被稱為子線程宦棺,子線程和主線程可以并行(同時(shí))執(zhí)行不同的任務(wù)。

原理:

1.同一時(shí)間黔帕,CPU只能處理一條線程代咸,只有一條線程在工作(執(zhí)行)
2.多線程并發(fā)(同時(shí)執(zhí)行,其實(shí)是CPU快速地在多條線程之間)調(diào)度(切換)
3.如果CPU調(diào)度線程的時(shí)間足夠快成黄,就造成了多線程并發(fā)執(zhí)行的假象
4.多核CPU呐芥,每個(gè)核心都可以同時(shí)處理不同任務(wù),從而真正達(dá)到了多線程并發(fā)執(zhí)行任務(wù)
5.線程非常多:
1> CPU會(huì)在N多條線程之間調(diào)度奋岁,CPU會(huì)累死思瘟,消耗大量的CPU資源
2> 每條線程被調(diào)度執(zhí)行的頻次會(huì)降低(線程的執(zhí)行效率降低)

多線程優(yōu)點(diǎn)

1.能適當(dāng)提高程序的執(zhí)行效率
2.能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)

多線程缺點(diǎn)

1.開(kāi)啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下闻伶,主線程占用1M,子線程占用512KB)滨攻,如果開(kāi)啟大量的線程,會(huì)占用大量的內(nèi)存空間蓝翰,降低程序的性能
2.線程越多光绕,CPU在調(diào)度線程上的開(kāi)銷就越大
3.程序設(shè)計(jì)更加復(fù)雜:比如線程之間的通信,多線程的數(shù)據(jù)共享

多線程在iOS開(kāi)發(fā)中的應(yīng)用

1.什么是主線程
1> 一個(gè)iOS程序運(yùn)行后畜份,默認(rèn)會(huì)開(kāi)啟一條線程诞帐,稱為"主線程"或"UI線程"
2> 每一個(gè)進(jìn)程都有一個(gè)獨(dú)立的主線程
2.主線程的主要作用
1> 顯示\刷新UI界面
2> 處理UI事件 (比如點(diǎn)擊事件、滾動(dòng)事件爆雹、拖拽事件等)
3.主線程的使用注意
1> 別將比較耗時(shí)的操作放到主線程中
2> 耗時(shí)操作會(huì)卡住主線程停蕉,嚴(yán)重影響UI的流暢度愕鼓,給用戶一種"卡"的壞體驗(yàn)
例如:首先隨便在viewController上拖一個(gè)textView或者是寫一個(gè)button的點(diǎn)擊事件,然后在viewDidLoad里寫上一個(gè)稍微耗時(shí)的操作:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    for (int i = 0; i<10000; i++) {
        NSLog(@"---------%d", i);
    }
}

那么當(dāng)點(diǎn)擊頁(yè)面后快速的滑動(dòng)textView谷徙,這時(shí)會(huì)發(fā)現(xiàn)拒啰,它是沒(méi)反應(yīng)的

阻塞主線程.gif

一直等到for循環(huán)走完之后,才會(huì)有反應(yīng)完慧,繼續(xù)執(zhí)行操作谋旦,這就是阻塞主線程。

多線程的實(shí)現(xiàn)方案

技術(shù)方案 簡(jiǎn)介 語(yǔ)言 線程聲明周期 使用頻率
pathread 一套通用的多線程API</br> 適用于Unix\Linux\Windows等系統(tǒng)</br>跨平臺(tái)\可移植</br>使用難度大 C 程序員管理 幾乎不用
NSThread 使用更加面向?qū)ο?lt;/br>簡(jiǎn)單易用屈尼,可直接操作線程對(duì)象 OC 程序員管理 偶爾使用
GCD 旨在替代NSThread等線程技術(shù)</br>充分利用設(shè)備的多核 C 自動(dòng)管理 經(jīng)常使用
NSOperation 基于GCD(底層是GCD)</br>比GCD多了一些簡(jiǎn)單實(shí)用的功能</br>實(shí)用更加面向?qū)ο?/td> OC 自動(dòng)管理 經(jīng)常使用

pthread的創(chuàng)建:

    //創(chuàng)建
    pthread_t myRestrict;
    pthread_create(&myRestrict, NULL, run, NULL);
void *run(void *data){
    NSLog(@"*******%@",[NSThread currentThread]);
    return NULL;
}

因?yàn)椴怀S貌嶙牛蛔龆嘟忉專瑥拇蛴〗Y(jié)果可以看出脾歧,開(kāi)辟了子線程

NSThread創(chuàng)建和啟動(dòng)線程的3種方式:

#pragma mark ---- 1.使用NSThread手動(dòng)開(kāi)辟子線程 ----
//    1甲捏、創(chuàng)建線程(NSThread)
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downLoad:) object:"http//:abcde.png"];
//線程的名字
thread.name = @"下載線程";
//    2、開(kāi)啟線程
        [thread start];
//    3鞭执、關(guān)閉線程(可寫可不寫)
        [NSThread exit];
//    4司顿、取消線程(實(shí)際上就是做了個(gè)標(biāo)記,表示被取消了)
        [thread cancel];
#pragma mark ---- 2.使用NSThread自動(dòng)開(kāi)辟子線程(無(wú)需手動(dòng)) ----
 [NSThread detachNewThreadSelector:@selector(downLoad:) toTarget:self withObject:"http//:abcde.png"];
[thread start];
#pragma mark ---- 3.使用NSObject開(kāi)辟子線程(在后臺(tái)執(zhí)行某個(gè)方法)兄纺,也叫隱式創(chuàng)建(無(wú)需手動(dòng)) ----
/*
 [self performSelector:@selector(downLoad:) withObject:@"http//:abcde.png"];
//相當(dāng)于下面這段代碼
   [self downLoad:@"http//:abcde.png"];
#因此大溜,這個(gè)方法是不開(kāi)辟子線程的
*/
[self performSelectorInBackground:@selector(downLoad:) withObject:@"http//:abcde.png"];
//這個(gè)方法開(kāi)辟子線程

常見(jiàn)的方法:

1> 獲得當(dāng)前線程
+ (NSThread *)currentThread;
 2> 獲得主線程
+ (NSThread *)mainThread;
 3>睡眠(暫停)線程
 +(void)sleepUntilDate:(NSDate *)date;
 + (void)sleepForTimeInterval:(NSTimeInterval)ti
------
//回到主線程刷新數(shù)據(jù)
    [self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];

-(void)reloadData{
    //刷新UI
    if ([NSThread isMainThread]) {
        //這里做一些需要的操作
    }
}

利用上面的一些方法,可以做一些簡(jiǎn)單的多線程之間的通信估脆,例如開(kāi)辟子線程下載圖片钦奋,回到主線程刷新,這里不再操作

多線程的安全隱患

1.資源共享
1> 1塊資源可能會(huì)被多個(gè)線程共享疙赠,也就是多個(gè)線程可能會(huì)訪問(wèn)同一塊資源
2> 比如多個(gè)線程訪問(wèn)同一個(gè)對(duì)象付材、同一個(gè)變量、同一個(gè)文件
2.當(dāng)多個(gè)線程訪問(wèn)同一塊資源時(shí)圃阳,很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問(wèn)題

安全隱患解決--加鎖(互斥鎖)

1.互斥鎖的優(yōu)缺點(diǎn)
1> 優(yōu)點(diǎn):能有效的防止多線程搶奪資源造成的數(shù)據(jù)安全問(wèn)題
2> 缺點(diǎn):需要消耗大量的CPU資源
2.互斥鎖的使用前提:多條線程搶奪同一塊資源
線程同步的意思是:多條線程實(shí)在同一條線上執(zhí)行(按順序的執(zhí)行任務(wù))
互斥鎖厌衔,就是使用了線程同步技術(shù)
單純的靠文字?jǐn)⑹隹赡軙?huì)比較難理解,可以參考以下例子:寫一個(gè)賣票的簡(jiǎn)單程序

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.leftTicketCount = 50;
    
    self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread1.name = @"1號(hào)窗口";
    
    self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread2.name = @"2號(hào)窗口";
    
    self.thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread3.name = @"3號(hào)窗口";
    

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.thread1 start];
    [self.thread2 start];
    [self.thread3 start];
}

/**
 *  賣票
 */
- (void)saleTicket
{
    while (1) {
      
            int count = self.leftTicketCount;
            if (count > 0) {
                [NSThread sleepForTimeInterval:0.05];
                
                self.leftTicketCount = count - 1;
                
                NSLog(@"%@賣了一張票, 剩余%d張票", [NSThread currentThread].name, self.leftTicketCount);
            } else {
                return; // 退出循環(huán)
            }
    }
}

運(yùn)行結(jié)果:


賣票.png

這就是多線程訪問(wèn)同一塊資源造成的
加鎖:在剛才的方法里加上@synchronized

- (void)saleTicket
{
    while (1) {
        // ()小括號(hào)里面放的是鎖對(duì)象
        @synchronized(self) { // 開(kāi)始加鎖
            int count = self.leftTicketCount;
            if (count > 0) {
                [NSThread sleepForTimeInterval:0.05];
                
                self.leftTicketCount = count - 1;
                
                NSLog(@"%@賣了一張票, 剩余%d張票", [NSThread currentThread].name, self.leftTicketCount);
            } else {
                return; // 退出循環(huán)
            }
        } // 解鎖
    }
}


GCD的內(nèi)容較多捍岳,我會(huì)在下篇文章里詳細(xì)介紹

NSOperation

1.首先富寿,它是一個(gè)抽象類,所以執(zhí)行任務(wù)的是它的子類:NSInvocationOperation和NSBlockOperation(還有:自定義子類繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的?法)祟同。這兩個(gè)子類作喘,相當(dāng)于一個(gè)方法選擇器“prefromSelector()”,由它倆本身發(fā)起的任務(wù)晕城,并不是在子線程中執(zhí)行泞坦。
2.NSOperation和它的子類,本身并不會(huì)進(jìn)行線程的創(chuàng)建砖顷,所以贰锁,在他們的任務(wù)方法中打印當(dāng)前線程赃梧,顯示為主線程
3.NSOperation和它的子類,只是一個(gè)操作豌熄,本身沒(méi)有主線程授嘀、子線程之分,可以在任何線程中使用锣险,通常和NSOperationQueue結(jié)合使用蹄皱。

NSOperationQueue

1、一個(gè)NSOperationQueue操作隊(duì)列芯肤,就相當(dāng)于一個(gè)線程管理器巷折,將NSOperation和子類的對(duì)象放入隊(duì)列中,然后由隊(duì)列負(fù)責(zé)派發(fā)任務(wù)崖咨,所以NSOperationQueue并不是一個(gè)線程锻拘。但是,你可以設(shè)置隊(duì)列中運(yùn)行的線程的數(shù)量

---- 優(yōu)點(diǎn) ----
不需要手動(dòng)關(guān)聯(lián)線程击蹲,只需要把精力放在自己要執(zhí)行的操作上面署拟。

---- 缺點(diǎn) ----
它是基于OC對(duì)象的,那么相對(duì)于C函數(shù)來(lái)說(shuō)效率要低

配合使用NSOperation和NSOperationQueue也能實(shí)現(xiàn)多線程編程


//子類一:NSInvocationOperation
    NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(hehehe) object:nil];
//operation在單獨(dú)使用的時(shí)候,需要手動(dòng)調(diào)用開(kāi)啟方法
  [operation start];
#operation直接調(diào)用start歌豺,是同步執(zhí)行(在當(dāng)前線程中執(zhí)行操作)推穷,說(shuō)白了就相當(dāng)于[self hehehe];并沒(méi)有什么卵用
//配合NSOperationQueue使用:
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(hehehe) object:nil];
[queue addOperation:operation];//********注意:如果搭配了NSOperationQueue中的add方法創(chuàng)建多線程的話,就不需要使用satrt方法,否則會(huì)崩潰
//子類二:NSBlockOperation
//任務(wù)數(shù)量 > 1,才會(huì)開(kāi)始異步執(zhí)行
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block:%@",[NSThread currentThread]);
        NSLog(@"block:%@",[NSThread mainThread]);
        NSLog(@"block:%d",[NSThread isMainThread]);
        NSLog(@"*********");
    }];
//開(kāi)啟
    [blockOperation start];
//配合NSOperationQueue使用:
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block:%@",[NSThread currentThread]);
        NSLog(@"block:%@",[NSThread mainThread]);
        NSLog(@"block:%d",[NSThread isMainThread]);
        NSLog(@"*********");
    }];
[queue addOperation:blockOperation];
//簡(jiǎn)單的做法:
 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
        NSLog(@"*******%@", [NSThread currentThread]);
    }];
#------設(shè)置最大并發(fā)數(shù)-----
 //當(dāng)設(shè)置最大并發(fā)數(shù)為1時(shí) :也可叫做串行,順序執(zhí)行
    //當(dāng)設(shè)置最大并發(fā)數(shù)大于1時(shí):叫并行世曾,多條通道同時(shí)進(jìn)行各自的任務(wù)缨恒,互不影響谴咸。
    queue.maxConcurrentOperationCount = 3;

除了GCD轮听,其他兩種主要的創(chuàng)建多線程的方法基本已經(jīng)介紹完了,如果覺(jué)得有什么問(wèn)題或者錯(cuò)誤岭佳,歡迎留言或發(fā)簡(jiǎn)信Q !

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末珊随,一起剝皮案震驚了整個(gè)濱河市述寡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叶洞,老刑警劉巖鲫凶,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異衩辟,居然都是意外死亡螟炫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門艺晴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)昼钻,“玉大人掸屡,你說(shuō)我怎么就攤上這事∪黄溃” “怎么了仅财?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)碗淌。 經(jīng)常有香客問(wèn)我盏求,道長(zhǎng),這世上最難降的妖魔是什么亿眠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任风喇,我火速辦了婚禮,結(jié)果婚禮上缕探,老公的妹妹穿的比我還像新娘魂莫。我一直安慰自己,他們只是感情好爹耗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布耙考。 她就那樣靜靜地躺著,像睡著了一般潭兽。 火紅的嫁衣襯著肌膚如雪倦始。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,231評(píng)論 1 299
  • 那天山卦,我揣著相機(jī)與錄音鞋邑,去河邊找鬼。 笑死账蓉,一個(gè)胖子當(dāng)著我的面吹牛枚碗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铸本,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肮雨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了箱玷?” 一聲冷哼從身側(cè)響起怨规,我...
    開(kāi)封第一講書(shū)人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锡足,沒(méi)想到半個(gè)月后波丰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舶得,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年掰烟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡媚赖,死狀恐怖霜瘪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惧磺,我是刑警寧澤颖对,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站磨隘,受9級(jí)特大地震影響缤底,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜番捂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一个唧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧设预,春花似錦徙歼、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至宾符,卻和暖如春酿秸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背魏烫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工辣苏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哄褒。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓稀蟋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親读处。 傳聞我的和親對(duì)象是個(gè)殘疾皇子糊治,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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