多線程-NSOperation,NSOperationQueue

文章優(yōu)先發(fā)布于小小廚師的廚房

NSOperation,NSOperationQueue

demo洗贰。最快熟悉的方式就是自己碼一遍。

NSOperation

NSOperation是一個抽象類,我們可以直接使用其子類NSInvocationOperationNSBlockOperation蔬胯,或者封裝NSOperation子類來添加要在線程中執(zhí)行的操作芥炭。默認情況下NSOperation單獨使用時執(zhí)行同步操作。

NSInvocationOperation

操作在當前線程中執(zhí)行愿伴,不開啟新線程。

  NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(p_task1) object:nil];
  [op1 start];

NSBlockOperation

  • 如果封裝多個操作电湘,會自動開啟線程隔节,線程數(shù)由系統(tǒng)決定。封裝多個操作時不受串行隊列控制寂呛。
    創(chuàng)建操作:
  NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"--->blockOp1-Thread:%@",[NSThread currentThread]);
    }];
    [op start];

添加操作:通過- (void)addExecutionBlock:(void (^)(void))block;NSBlockOperation添加額外的操作官帘。這些操作可以在不同的線程中并發(fā)執(zhí)行,線程的切換由系統(tǒng)決定昧谊。如果添加的操作較多刽虹,blockOperationWithBlock:中的操作可能會在其他線程中執(zhí)行。

自定義NSOperation子類

通過重寫mainstart方法來定義自己的NSOperation對象呢诬。

// 當前線程執(zhí)行

@implementation CustomOperation

- (void)main {
    if (!self.cancelled) {
        NSLog(@"--->customMainOp-Thread:%@",[NSThread currentThread]);
    }
}

@end

并發(fā)執(zhí)行

需要重寫的方法:
必須:

  • start:是一個operation的起點涌哲。重寫時不要調(diào)用父類的方法。
  • isExecuting尚镰、isFinished:operation狀態(tài)阀圾。當值發(fā)生變化時需要生成相應的KVO通知,以便外界能夠觀察狀態(tài)變化狗唉。
  • isAsynchronous:返回是否并發(fā)初烘。

可選:

  • main:實現(xiàn)改operation相關(guān)聯(lián)的任務。用main方法實現(xiàn)任務可使 設(shè)置代碼 任務代碼 得到分離,從而使operation的結(jié)構(gòu)更加清晰肾筐。
@implementation ConcurrentOperation

@synthesize executing = _executing;
@synthesize finished = _finished;

- (BOOL)isExecuting {
    return _executing;
}

- (BOOL)isFinished {
    return _finished;
}

- (BOOL)isAsynchronous {
    return YES;
}

- (void)start {
    if (self.cancelled) {
        [self willChangeValueForKey:@"isFinished"];
        _finished = YES;
        [self willChangeValueForKey:@"isFinished"];
        return;
    }
    [self willChangeValueForKey:@"isExecuting"];
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
    _executing = YES;
    [self willChangeValueForKey:@"isExecuting"];
}

- (void)main {
    NSLog(@"--->concurrentOp-Thread:%@",[NSThread currentThread]);
    [self willChangeValueForKey:@"isExecuting"];
    _executing = NO;
    [self didChangeValueForKey:@"isExecuting"];
    
    [self willChangeValueForKey:@"isFinished"];
    _finished  = YES;
    [self didChangeValueForKey:@"isFinished"];
}
@end

  • 注意:當operation被cancel時哆料,仍然需要手動出發(fā)isFinishedKVO通知。因為當一個Operation依賴其他Operation時吗铐,它會觀察所有其他Operation的isFinished的值的變化东亦,只有當它依賴的所有Operation的isFinished的值為YES時,這個operation才能開始執(zhí)行唬渗。

NSOperationQueue

  • NSOperation配合NSOperationQueue來實現(xiàn)多線程典阵。NSOperationQueue對于添加到隊列中的操作,首先進入準備就緒的狀態(tài)镊逝,然后進入就緒狀態(tài)壮啊,就緒狀態(tài)的操作的開始執(zhí)行順序由操作之間相對的優(yōu)先級決定。
  • 理論上我們可以創(chuàng)建任意數(shù)量的OperationQueue撑蒜,但數(shù)量越多并不意味著我們就能同時執(zhí)行越多的Operation歹啼。因為并發(fā)的Operation數(shù)量由系統(tǒng)決定,系統(tǒng)會根據(jù)自身動態(tài)調(diào)整减江。
  • 操作隊列通過設(shè)置maxConcurrentOperationCount控制并發(fā)、串行捻爷,默認值為-1辈灼,并發(fā)執(zhí)行,線程數(shù)由系統(tǒng)決定也榄。為1時為串行隊列巡莹。

隊列類型:

  • 主隊列:[NSOperationQueue mainQueue];
  • 異步主隊列:[[NSOperationQueue alloc] init];

操作加入到隊列

addOperation

    NSOperationQueue *customQueue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(p_task1) object:nil];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(2);
        NSLog(@"--->OpQueue1-Thread:%@",[NSThread currentThread]);
    }];
    [customQueue addOperation:op1];
    [customQueue addOperation:op3];

addOperationWithBlock

    NSOperationQueue *customQueue = [[NSOperationQueue alloc] init];
    [customQueue addOperationWithBlock:^{
        NSLog(@"--->BlockOpQueue1-Thread:%@",[NSThread currentThread]);
    }];

并發(fā)控制

operationQueue的并發(fā)控制由maxConcurrentOperationCount來控制。

  • 默認值NSOperationQueueDefaultMaxConcurrentOperationCount甜紫,-1,系統(tǒng)控制并發(fā)數(shù)量降宅。
  • 設(shè)置為1時,串行隊列囚霸,操作依次執(zhí)行腰根,但操作處于的線程由系統(tǒng)控制
  • >1 時拓型,最大并發(fā)數(shù)不能大于系統(tǒng)能提供的并發(fā)數(shù)额嘿。

NSOperation操作依賴

NSOperation依賴關(guān)系由自身管理,與Queue無關(guān)。
添加是在Operation操作執(zhí)行之前添加劣挫。
防止循環(huán)依賴

  • 添加依賴:- (void)addDependency:(NSOperation *)op;
  • 移除依賴:- (void)removeDependency:(NSOperation *)op;

NSOperation優(yōu)先級 - 針對已加入到到隊列中的操作有效册养。

  • operation的執(zhí)行順序的第一要素是它們的isReady狀態(tài),其次是它們在隊列中的優(yōu)先級压固。優(yōu)先級只決定isReady為YES的Operation的執(zhí)行順序球拦。當operation依賴未執(zhí)行完時,isReady為NO。
  • 隊列優(yōu)先級只應用于相同的Queue中的Operation之間坎炼。
  • Operation依賴 > Operation優(yōu)先級愧膀。
  • 默認優(yōu)先級:NSOperationQueuePriorityNormal
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

線程間通信

  NSOperationQueue *opQ = [[NSOperationQueue alloc] init];
    [opQ addOperationWithBlock:^{
        sleep(1);
        NSLog(@"--->1-Thread:%@",[NSThread currentThread]);
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"--->2-Thread:%@",[NSThread currentThread]);
        }];
    }];

線程安全

若每個線程中對全局變量点弯、靜態(tài)變量只有讀操作扇调,而無寫操作,一般來說抢肛,這個全局變量是線程安全的狼钮;若有多個線程同時執(zhí)行寫操作(更改變量),一般都需要考慮線程同步捡絮,否則的話就可能影響線程安全熬芜。

線程安全解決:加鎖

  • @synchronized
  • NSLock、NSRecrusiveLock福稳、NSCondition涎拉、NSConditionLock
  • pthread_mutex
  • dispatch_semaphore
  • atomic

性能對比:


lock_benchmark.png

參考

https://bujige.net/blog/iOS-Complete-learning-NSOperation.html
http://blog.leichunfeng.com/blog/2015/07/29/ios-concurrency-programming-operation-queues/
https://blog.ibireme.com/2016/01/16/spinlock_is_unsafe_in_ios/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市的圆,隨后出現(xiàn)的幾起案子鼓拧,更是在濱河造成了極大的恐慌,老刑警劉巖越妈,帶你破解...
    沈念sama閱讀 223,207評論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件季俩,死亡現(xiàn)場離奇詭異,居然都是意外死亡梅掠,警方通過查閱死者的電腦和手機酌住,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評論 3 400
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阎抒,“玉大人酪我,你說我怎么就攤上這事∏胰” “怎么了都哭?”我有些...
    開封第一講書人閱讀 170,031評論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長逞带。 經(jīng)常有香客問我质涛,道長,這世上最難降的妖魔是什么掰担? 我笑而不...
    開封第一講書人閱讀 60,334評論 1 300
  • 正文 為了忘掉前任汇陆,我火速辦了婚禮,結(jié)果婚禮上带饱,老公的妹妹穿的比我還像新娘毡代。我一直安慰自己阅羹,他們只是感情好,可當我...
    茶點故事閱讀 69,322評論 6 398
  • 文/花漫 我一把揭開白布教寂。 她就那樣靜靜地躺著捏鱼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪酪耕。 梳的紋絲不亂的頭發(fā)上导梆,一...
    開封第一講書人閱讀 52,895評論 1 314
  • 那天,我揣著相機與錄音迂烁,去河邊找鬼看尼。 笑死,一個胖子當著我的面吹牛盟步,可吹牛的內(nèi)容都是我干的藏斩。 我是一名探鬼主播,決...
    沈念sama閱讀 41,300評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼却盘,長吁一口氣:“原來是場噩夢啊……” “哼狰域!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起黄橘,我...
    開封第一講書人閱讀 40,264評論 0 277
  • 序言:老撾萬榮一對情侶失蹤兆览,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后塞关,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抬探,經(jīng)...
    沈念sama閱讀 46,784評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,870評論 3 343
  • 正文 我和宋清朗相戀三年描孟,在試婚紗的時候發(fā)現(xiàn)自己被綠了驶睦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砰左。...
    茶點故事閱讀 40,989評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡匿醒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出缠导,到底是詐尸還是另有隱情廉羔,我是刑警寧澤,帶...
    沈念sama閱讀 36,649評論 5 351
  • 正文 年R本政府宣布僻造,位于F島的核電站憋他,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏髓削。R本人自食惡果不足惜竹挡,卻給世界環(huán)境...
    茶點故事閱讀 42,331評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望立膛。 院中可真熱鬧揪罕,春花似錦梯码、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至框往,卻和暖如春鳄抒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背椰弊。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評論 1 275
  • 我被黑心中介騙來泰國打工许溅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人男应。 一個月前我還...
    沈念sama閱讀 49,452評論 3 379
  • 正文 我出身青樓闹司,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沐飘。 傳聞我的和親對象是個殘疾皇子游桩,可洞房花燭夜當晚...
    茶點故事閱讀 45,995評論 2 361

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