iOS多線程2

1.線程的狀態(tài):
線程的狀態(tài).png
2.GCD
//1.創(chuàng)建隊(duì)列
/*
參數(shù)1: C語言字符串,是一個(gè)標(biāo)簽,用來區(qū)分隊(duì)列
參數(shù)2: DISPATCH_QUEUE_CONCURRENT -> 并發(fā)隊(duì)列
      DISPATCH_QUEUE_SERIAL -> 串行隊(duì)列
 */
//串行隊(duì)列 串行隊(duì)列只會(huì)開辟一個(gè)子線程
 dispatch_queue_t queue = dispatch_queue_create("GCD", DISPATCH_QUEUE_SERIAL);
//并發(fā)隊(duì)列 并發(fā)隊(duì)列可以開辟多個(gè)子線程
dispatch_queue_t queue = dispatch_queue_create("GCD", DISPATCH_QUEUE_CONCURRENT);
//主隊(duì)列 添加到主隊(duì)列中的任務(wù)意味著任務(wù)都會(huì)在主線程中執(zhí)行
dispatch_queue_t queue = dispatch_get_main_queue();

/*
參數(shù)1: 隊(duì)列的優(yōu)先級(jí) 
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND 
優(yōu)先級(jí)從高到低的排序

參數(shù)2: 隊(duì)列的標(biāo)簽
 */
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);


//2.封裝任務(wù)
/*
參數(shù)1: queue 把任務(wù)添加到這個(gè)隊(duì)列中
參數(shù)2: block塊 用于添加任務(wù)
 */
//異步函數(shù)
dispatch_async(queue, ^{
//在這里執(zhí)行想要執(zhí)行的任務(wù),GCD會(huì)自動(dòng)根據(jù)上下文代碼判斷是否需要開辟線程
});
//同步函數(shù)
dispatch_sync(queue, ^{
//同步函數(shù)不開線程,直接再主線程中執(zhí)行任務(wù)
});

注意: 不能再主線程中再次滴啊用同步行數(shù)并且把任務(wù)添加到主隊(duì)列,會(huì)導(dǎo)致線程死鎖

//如果當(dāng)前線程已經(jīng)是主線程,再調(diào)用這調(diào)用這個(gè)函數(shù)來回到主線程的話,會(huì)出現(xiàn)崩潰
dispatch_sync (dispatch_get_main_queue(), ^{
        NSLog(@"abc");
    });
3.線程安全問題

在進(jìn)行多線程開發(fā)的時(shí)候,經(jīng)常會(huì)發(fā)生不同的線程在同一時(shí)間對(duì)同一個(gè)數(shù)據(jù)進(jìn)行操作,那么這個(gè)時(shí)候我們需要對(duì)數(shù)據(jù)資源進(jìn)行加鎖,以保證數(shù)據(jù)源的安全:

假設(shè)我們有下面一種情況使用多線程對(duì)一個(gè)數(shù)字進(jìn)行不斷的減法

#pragma mark 線程安全
-(void)asyncMethod1 {
    dispatch_queue_t q = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(q , ^{
        [self subMethod:@"線程1"];
    });
    dispatch_async(q, ^{
        [self subMethod:@"線程2"];
    });
    dispatch_async(q, ^{
        [self subMethod:@"線程3"];
    });
    //真實(shí)的情況中可能會(huì)是在不同的地方都用了異步并行的方式調(diào)用了同一個(gè)方法
}

/#pragma mark 執(zhí)行減法操作
-(void)subMethod:(NSString *)stu{
    while (1) {
        if (self.numbers > 0) {
            for (int i = 0; i< 100000; i++) {}
            self.numbers -= 1;
            NSLog(@"%@使用了減法后剩余:%d",stu,self.numbers);
        }else{
            NSLog(@"%@發(fā)現(xiàn)減數(shù)為0",stu);
            break;
        }
    }
}

/*
當(dāng)調(diào)用 asyncMethod1 方法時(shí)
控制臺(tái)最后打印了如下數(shù)據(jù):
->線程1使用了減法后剩余:0
->線程1發(fā)現(xiàn)減數(shù)為0
->線程2使用了減法后剩余:-1
->線程2發(fā)現(xiàn)減數(shù)為0
->線程3使用了減法后剩余:-2
->線程3發(fā)現(xiàn)減數(shù)為0
*/

我們明明已經(jīng)對(duì)numbers進(jìn)行判斷,只有大于0的時(shí)候才進(jìn)行減1操作,為什么還會(huì)出現(xiàn)負(fù)數(shù)呢?
因?yàn)楫?dāng)三條線程同時(shí)調(diào)用"-(void)subMethod:(NSString *)stu;"方法時(shí);
發(fā)現(xiàn)那個(gè)時(shí)候的"numbers = 1" 所有都同時(shí)進(jìn)行了 減 1 的操作;
如果此處是對(duì)數(shù)組進(jìn)行取值的,就會(huì)出現(xiàn)崩潰


//可以通過 @synchronized(鎖死的對(duì)象){} 進(jìn)行
@synchronized ( self.models<這個(gè)地方只能傳OC對(duì)象類型> ) {
       //在線程所內(nèi)對(duì)數(shù)據(jù)源進(jìn)行增加或減少的操作
}

//改寫后的減法操作
-(void)subMethod:(NSString *)stu{
while (1) {
   @synchronized ( self) {
       //在線程所內(nèi)對(duì)數(shù)據(jù)源進(jìn)行增加或減少的操作
           if (self.numbers > 0) {
            for (int i = 0; i< 100000; i++) {}
            self.numbers -= 1;
            NSLog(@"%@使用了減法后剩余:%d",stu,self.numbers);
        }else{
            NSLog(@"%@發(fā)現(xiàn)減數(shù)為0",stu);
            break;
        }
     }
   }
}
4.GCD線程?hào)艡趩栴}
#pragma mark 柵欄函數(shù)
-(void)method2{
    
    dispatch_queue_t q = dispatch_queue_create("柵欄函數(shù)", DISPATCH_QUEUE_CONCURRENT);
    
//注意,柵欄函數(shù)不能使用全局并發(fā)隊(duì)列
// dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(q, ^{NSLog(@"執(zhí)行任務(wù)一");});
    
    dispatch_async(q, ^{ NSLog(@"執(zhí)行任務(wù)二");});
    
    dispatch_barrier_async(q, ^{ NSLog(@"執(zhí)行柵欄任務(wù)");});
    
    dispatch_async(q, ^{NSLog(@"執(zhí)行任務(wù)三");});
}

/*上面的方法,在多線程任務(wù)重,會(huì)限制性任務(wù)一和二,(一/二的先后順序由系統(tǒng)控制)
當(dāng)任務(wù)一和任務(wù)二執(zhí)行完成以后,系統(tǒng)會(huì)再執(zhí)行任務(wù)柵欄函數(shù)后的任務(wù);
*/
5.GCD快速遍歷數(shù)組
NSArray *characters = @[@"a",@"b",@"c",@"d",@"e"];
//普通的遍歷方式
for (NSString *C in characters) {
        NSLog(@"%@--%@",C,[NSThread currentThread]);
    }
//普通的遍歷方式會(huì)在本來的線程中進(jìn)行串行遍歷

//GCD遍歷方
dispatch_queue_t q = dispatch_queue_create("并發(fā)隊(duì)列", DISPATCH_QUEUE_CONCURRENT);
/*
參數(shù)1:遍歷的次數(shù),數(shù)組的count屬性
參數(shù)2:隊(duì)列  <只能用并發(fā)隊(duì)列,如果用主隊(duì)列就會(huì)線程死鎖,如果是串行隊(duì)列,那就不會(huì)開多個(gè)子線程>
參數(shù)3:遍歷的 index
*/
dispatch_apply(characters.count, q, ^(size_t i) {
        NSString *c = characters[i];
        NSLog(@"%@--%@",c,[NSThread currentThread]);
    });

//使用GCD并發(fā)隊(duì)列遍歷,系統(tǒng)會(huì)開辟子線程對(duì)數(shù)組元素同時(shí)進(jìn)行遍歷,提升APP性能
5.GCD group 隊(duì)列組

GCD隊(duì)列組的可以用于監(jiān)聽隊(duì)列組中任務(wù)是否執(zhí)行完,執(zhí)行完后再執(zhí)行下一個(gè)操作

-(void)GCDgroupDemo{
//創(chuàng)建隊(duì)列
    dispatch_queue_t q = dispatch_queue_create("group", DISPATCH_QUEUE_CONCURRENT);
//創(chuàng)建隊(duì)列組
    dispatch_group_t g = dispatch_group_create();
    
    dispatch_group_async(g, q, ^{
        for (double i = 0; i<=1.0; i+=0.00002) {
            dispatch_sync(dispatch_get_main_queue(), ^{
                self.p1.progress = i;
            });
        }
    });
    //往隊(duì)列組里面添加隊(duì)列和任務(wù)
    dispatch_group_async(g, q, ^{
        for (double i = 0; i<=1.0; i+=0.00001) {
            dispatch_sync(dispatch_get_main_queue(), ^{
                self.p2.progress = i;
            });
        }
    });
//監(jiān)聽隊(duì)列執(zhí)行完成情況
dispatch_group_notify(g, q, ^{
        NSLog(@"%@",[NSThread currentThread]);
        for (double i = 0; i<=1.0; i+=0.00002) {
            dispatch_sync(dispatch_get_main_queue(), ^{
                self.p3.progress = i;
            });
        }
    });

NSOperation

1.NSOperation的基本概念

NSOperation 是蘋果封裝的一個(gè)對(duì)象類型的多線程方式;
NSOperation不需要用戶控制什么時(shí)候開線程執(zhí)行任務(wù)
NSOperation可以控制同一時(shí)間內(nèi)執(zhí)行的任務(wù)數(shù)量

2.NSOperationQueue的基本使用

使用NSOperation 和 NSOperationqueue 實(shí)現(xiàn)多線程的步驟
1.將需要執(zhí)行的任務(wù)封裝到NSOperation中
2.將NSOperation對(duì)象封添加NSOperationqueue中
3.系統(tǒng)自動(dòng)將NSOperationqueue中的NSOperation取出來
4.將NSOperation中封裝的任務(wù)放在線程中執(zhí)行

NSOperation的子類配合NSOperationQueue使用:
//1. NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];

//2. NSBlockOperation
__weak typeof(self) weakself = self;
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{[weakself operationDemoMethod]}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{[weakself operationDemoMethod]}];
NSBlockOperation *op6 = [NSBlockOperation blockOperationWithBlock:^{[weakself operationDemoMethod]}];

//NSOperationQueue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
[queue addOperation:op6];
3. NSOperation的線程依賴 和 監(jiān)聽
//1.封裝任務(wù)到NSOperation中
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];

//2.添加線程依賴 <依賴是可以跨隊(duì)列的,即: 不處于同一個(gè)NSOperationQueue中的NSOperation是可以使用依賴操作的>
[op2 addDependency:op1];
[op3 addDependency:op2];
/*
添加完這樣的線程依賴后,當(dāng)系統(tǒng)調(diào)取NSOperationQueue中的
任務(wù),就會(huì)按照依賴順序先執(zhí)行op1中的任務(wù),op1的任務(wù)執(zhí)行結(jié)
束以后,才會(huì)執(zhí)行op2中的任務(wù),同理,op3也依賴于op2
*/

//3.創(chuàng)建NSOperationQueue 并添加 NSOperation
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];

//4.線程的監(jiān)聽
op2.completionBlock = ^{
//op2執(zhí)行完成后,想要實(shí)現(xiàn)的操作可以直接再這里調(diào)用
//這個(gè)方法和線程的依賴是差不多的
};

注意:不能出現(xiàn)循環(huán)依賴

[op2 addDependency:op1];
[op1 addDependency:op2];
//這么寫,程序不會(huì)崩潰,但是op1 & op2 都不會(huì)執(zhí)行
3. NSOperationQueue的暫停/回復(fù)/取消/最大并發(fā)數(shù)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

/*控制queue的同時(shí)并發(fā)線程數(shù)是:3;
這里不能等同于控制只開3條線程;
queue隊(duì)列可以開辟超過3條線程,但是同一時(shí)間內(nèi)只會(huì)并發(fā)執(zhí)行3條
*/
queue.maxConcurrentOperationCount = 3;

//暫停隊(duì)列
/*
這里假設(shè)一個(gè)場(chǎng)景:
我們往隊(duì)列中添加了10個(gè)任務(wù),
隊(duì)列的最大并發(fā)數(shù)為:3個(gè)
當(dāng)隊(duì)列開始執(zhí)行任務(wù)的時(shí)候,但是又沒有執(zhí)行完成的時(shí)候,想暫停
那么我們可以使用一下方法實(shí)現(xiàn)NSOperationQueue的暫停
*/
[queue setSuspended:YES];

//同理,我們想要恢復(fù)NSOperationQueue的時(shí)候,只需要設(shè)置NO
[queue setSuspended:NO];

//注意:當(dāng)點(diǎn)擊暫停的時(shí)候,已經(jīng)開始執(zhí)行的任務(wù)會(huì)執(zhí)行完成
//NSOperationQueue的暫停原理是:停止調(diào)取queue中未開始執(zhí)行的任務(wù)

//NSOperationQueue 取消任務(wù)
[queue cancelAllOperations];
//被取消的NSOperationQueue 是不可再恢復(fù)執(zhí)行的,
4. NSOperationQueue 回到主線程操作

通常我們開辟子線程都是用來執(zhí)行耗時(shí)操作獲取數(shù)據(jù)的,一旦數(shù)據(jù)獲取,我們需要刷新UI展示給用戶看,這個(gè)時(shí)候需要回到主線程刷新UI

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
  //1.執(zhí)行耗時(shí)操作獲取數(shù)據(jù)
   .....
  //2.獲得數(shù)據(jù)以后,回到主線程刷新UI
 [[NSOperationQueue mainQueue] addOperationWithBlock:^{
         //在此處已經(jīng)回到主線程,可以直接執(zhí)行刷新UI的代碼
       }];            
}];

多線程的幾種情況已經(jīng)總結(jié)完成

如果您巧合看到這篇文章,想要交流可以直接留言,若有不足之處也請(qǐng)留言之處,十分感激;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,772評(píng)論 3 422
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,960評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,350評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,549評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,104評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,914評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,089評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,340評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評(píng)論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,834評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,106評(píng)論 2 375

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