多線程

  • NSThread
-(void)loadImageWithMultiThread{
    //方法1:使用對象方法
    //創(chuàng)建一個線程,第一個參數(shù)是請求的操作纪岁,第二個參數(shù)是操作方法的參數(shù)
//    NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
//    //啟動一個線程鞋仍,注意啟動一個線程并非就一定立即執(zhí)行黄痪,而是處于就緒狀態(tài),當(dāng)系統(tǒng)調(diào)度時才真正執(zhí)行
//    [thread start];
    
    //方法2:使用類方法
    [NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
}
  • NSObject分類擴展方法
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg:在后臺執(zhí)行一個操作崎页,本質(zhì)就是重新創(chuàng)建一個線程執(zhí)行當(dāng)前方法唧喉。
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait:在指定的線程上執(zhí)行一個方法,需要用戶創(chuàng)建一個線程對象四瘫。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait:在主線程上執(zhí)行一個方法(前面已經(jīng)使用過)。
  • NSInvocationOperation
-(void)loadImageWithMultiThread{
    /*創(chuàng)建一個調(diào)用操作
     object:調(diào)用方法參數(shù)
    */
    NSInvocationOperation *invocationOperation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
    //創(chuàng)建完NSInvocationOperation對象并不會調(diào)用欲逃,它由一個start方法啟動操作找蜜,但是注意如果直接調(diào)用start方法,則此操作會在主線程中調(diào)用稳析,一般不會這么操作,而是添加到NSOperationQueue中
//    [invocationOperation start];
    
    //創(chuàng)建操作隊列
    NSOperationQueue *operationQueue=[[NSOperationQueue alloc]init];
    //注意添加到操作隊后洗做,隊列會開啟一個線程執(zhí)行此操作
    [operationQueue addOperation:invocationOperation];
}
  • NSBlockOperation
//更新UI界面,此處調(diào)用了主線程隊列的方法(mainQueue是UI主線程)
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [self updateImageWithData:data andIndex:i];
    }];
#pragma mark 多線程下載圖片
-(void)loadImageWithMultiThread{
    int count=ROW_COUNT*COLUMN_COUNT;
    //創(chuàng)建操作隊列
    NSOperationQueue *operationQueue=[[NSOperationQueue alloc]init];
    operationQueue.maxConcurrentOperationCount=5;//設(shè)置最大并發(fā)線程數(shù)
    //創(chuàng)建多個線程用于填充圖片
    for (int i=0; i<count; ++i) {
        //方法1:創(chuàng)建操作塊添加到隊列
//        //創(chuàng)建多線程操作
//        NSBlockOperation *blockOperation=[NSBlockOperation blockOperationWithBlock:^{
//            [self loadImage:[NSNumber numberWithInt:i]];
//        }];
//        //創(chuàng)建操作隊列
//
//        [operationQueue addOperation:blockOperation];
        
        //方法2:直接使用操隊列添加操作
        [operationQueue addOperationWithBlock:^{
            [self loadImage:[NSNumber numberWithInt:i]];
        }];
        
    }
}
  • NSOperation可以設(shè)置依賴線程
-(void)loadImageWithMultiThread{
    int count=ROW_COUNT*COLUMN_COUNT;
    //創(chuàng)建操作隊列
    NSOperationQueue *operationQueue=[[NSOperationQueue alloc]init];
    operationQueue.maxConcurrentOperationCount=5;//設(shè)置最大并發(fā)線程數(shù)
    
    NSBlockOperation *lastBlockOperation=[NSBlockOperation blockOperationWithBlock:^{
        [self loadImage:[NSNumber numberWithInt:(count-1)]];
    }];
    //創(chuàng)建多個線程用于填充圖片
    for (int i=0; i<count-1; ++i) {
        //方法1:創(chuàng)建操作塊添加到隊列
        //創(chuàng)建多線程操作
        NSBlockOperation *blockOperation=[NSBlockOperation blockOperationWithBlock:^{
            [self loadImage:[NSNumber numberWithInt:i]];
        }];
        //設(shè)置依賴操作為最后一張圖片加載操作
        [blockOperation addDependency:lastBlockOperation];
        
        [operationQueue addOperation:blockOperation];
        
    }
    //將最后一個圖片的加載操作加入線程隊列
    [operationQueue addOperation:lastBlockOperation];
}
  • GCD
    異步執(zhí)行并發(fā)隊列不會調(diào)用主線程:
for (int i = 0; i < 10; i++) {
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"%d ---- %@",i, [NSThread currentThread]);
        });
        
    }

dispatch_semaphore_t: 每當(dāng)發(fā)送一個信號通知,則信號量+1彰居;每當(dāng)發(fā)送一個等待信號時信號量-1,诚纸;如果信號量為0則信號會處于等待狀態(tài),直到信號量大于0開始執(zhí)行陈惰。

/*初始化信號量
     參數(shù)是信號量初始值
     */
    _semaphore=dispatch_semaphore_create(1);
    
/*信號等待
第二個參數(shù):等待時間
*/
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
    if (_imageNames.count>0) {
        name=[_imageNames lastObject];
        [_imageNames removeObject:name];
    }
    //信號通知
    dispatch_semaphore_signal(_semaphore);

NSLock:

//初始化鎖對象
_lock=[[NSLock alloc]init];
//加鎖
[_lock lock];
    if (_imageNames.count>0) {
        name=[_imageNames lastObject];
        [_imageNames removeObject:name];
    }
//使用完解鎖
[_lock unlock];

@synchronized代碼塊: 如下代碼,self為同步對象,是以該對象是否為加鎖狀態(tài)來判斷該代碼塊是否應(yīng)該加鎖. 如果2個不同的實例對象,那么用self的話,就不會相互起作用,因為self不是同一個了.

//線程同步
    @synchronized(self){
        if (_imageNames.count>0) {
            name=[_imageNames lastObject];
            [NSThread sleepForTimeInterval:0.001f];
            [_imageNames removeObject:name];
        }
    }

NSCondition: NSCondition實現(xiàn)了NSLocking協(xié)議畦徘,所以它本身也有l(wèi)ock和unlock方法,因此也可以將它作為NSLock解決線程同步問題奴潘,此時使用方法跟NSLock沒有區(qū)別旧烧,只要在線程開始時加鎖,取得資源后釋放鎖即可画髓,這部分內(nèi)容比較簡單在此不再演示掘剪。當(dāng)然,單純解決線程同步問題不是NSCondition設(shè)計的主要目的奈虾,NSCondition更重要的是解決線程之間的調(diào)度關(guān)系(當(dāng)然夺谁,這個過程中也必須先加鎖廉赔、解鎖)。NSCondition可以調(diào)用wati方法控制某個線程處于等待狀態(tài)匾鸥,直到其他線程調(diào)用signal(此方法喚醒一個線程蜡塌,如果有多個線程在等待則任意喚醒一個)或者broadcast(此方法會喚醒所有等待線程)方法喚醒該線程才能繼續(xù)。

//
//  線程控制
//  MultiThread
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainViewController.h"
#import "KCImageData.h"
#define ROW_COUNT 5
#define COLUMN_COUNT 3
#define ROW_HEIGHT 100
#define ROW_WIDTH ROW_HEIGHT
#define CELL_SPACING 10
#define IMAGE_COUNT 9

@interface KCMainViewController (){
    NSMutableArray *_imageViews;
    NSCondition *_condition;
}

@end

@implementation KCMainViewController

#pragma mark - 事件
- (void)viewDidLoad {
    [super viewDidLoad];

    [self layoutUI];
}

#pragma mark - 內(nèi)部私有方法
#pragma mark 界面布局
-(void)layoutUI{
    //創(chuàng)建多個圖片控件用于顯示圖片
    _imageViews=[NSMutableArray array];
    for (int r=0; r<ROW_COUNT; r++) {
        for (int c=0; c<COLUMN_COUNT; c++) {
            UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING                           ), ROW_WIDTH, ROW_HEIGHT)];
            imageView.contentMode=UIViewContentModeScaleAspectFit;
            [self.view addSubview:imageView];
            [_imageViews addObject:imageView];
            
        }
    }
    
    UIButton *btnLoad=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    btnLoad.frame=CGRectMake(50, 500, 100, 25);
    [btnLoad setTitle:@"加載圖片" forState:UIControlStateNormal];
    [btnLoad addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btnLoad];
    
    UIButton *btnCreate=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    btnCreate.frame=CGRectMake(160, 500, 100, 25);
    [btnCreate setTitle:@"創(chuàng)建圖片" forState:UIControlStateNormal];
    [btnCreate addTarget:self action:@selector(createImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btnCreate];
    
    //創(chuàng)建圖片鏈接
    _imageNames=[NSMutableArray array];
    
    //初始化鎖對象
    _condition=[[NSCondition alloc]init];
    
    _currentIndex=0;
    
}


#pragma mark 創(chuàng)建圖片
-(void)createImageName{
    [_condition lock];
    //如果當(dāng)前已經(jīng)有圖片了則不再創(chuàng)建勿负,線程處于等待狀態(tài)
    if (_imageNames.count>0) {
        NSLog(@"createImageName wait, current:%i",_currentIndex);
        [_condition wait];
    }else{
        NSLog(@"createImageName work, current:%i",_currentIndex);
        //生產(chǎn)者馏艾,每次生產(chǎn)1張圖片
        [_imageNames addObject:[NSString stringWithFormat:@"http://images.cnblogs.com/cnblogs_com/kenshincui/613474/o_%i.jpg",_currentIndex++]];
        
        //創(chuàng)建完圖片則發(fā)出信號喚醒其他等待線程
        [_condition signal];
    }
    [_condition unlock];
}

#pragma mark 加載圖片并將圖片顯示到界面
-(void)loadAnUpdateImageWithIndex:(int )index{
    //請求數(shù)據(jù)
    NSData *data= [self requestData:index];
    //更新UI界面,此處調(diào)用了GCD主線程隊列的方法
    dispatch_queue_t mainQueue= dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{
        UIImage *image=[UIImage imageWithData:data];
        UIImageView *imageView= _imageViews[index];
        imageView.image=image;
    });
}

#pragma mark 請求圖片數(shù)據(jù)
-(NSData *)requestData:(int )index{
    NSData *data;
    NSString *name;
    name=[_imageNames lastObject];
    [_imageNames removeObject:name];
    if(name){
        NSURL *url=[NSURL URLWithString:name];
        data=[NSData dataWithContentsOfURL:url];
    }
    return data;
}

#pragma mark 加載圖片
-(void)loadImage:(NSNumber *)index{
    int i=(int)[index integerValue];
    //加鎖
    [_condition lock];
    //如果當(dāng)前有圖片資源則加載,否則等待
    if (_imageNames.count>0) {
        NSLog(@"loadImage work,index is %i",i);
        [self loadAnUpdateImageWithIndex:i];
        [_condition broadcast];
    }else{
        NSLog(@"loadImage wait,index is %i",i);
        NSLog(@"%@",[NSThread currentThread]);
        //線程等待
        [_condition wait];
        NSLog(@"loadImage resore,index is %i",i);
        //一旦創(chuàng)建完圖片立即加載
        [self loadAnUpdateImageWithIndex:i];
    }
    //解鎖
    [_condition unlock];
}


#pragma mark - UI調(diào)用方法
#pragma mark 異步創(chuàng)建一張圖片鏈接
-(void)createImageWithMultiThread{
    dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //創(chuàng)建圖片鏈接
    dispatch_async(globalQueue, ^{
        [self createImageName];
    });
}

#pragma mark 多線程下載圖片
-(void)loadImageWithMultiThread{
    int count=ROW_COUNT*COLUMN_COUNT;
    dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    for (int i=0; i<count; ++i) {
        //加載圖片
        dispatch_async(globalQueue, ^{
            [self loadImage:[NSNumber numberWithInt:i]];
        });
    }
}
@end

在上面的代碼中l(wèi)oadImage:方法是消費者奴愉,當(dāng)在界面中點擊“加載圖片”后就創(chuàng)建了15個消費者線程琅摩。在這個過程中每個線程進入圖片加載方法之后都會先加鎖,加鎖之后其他進程是無法進入“加鎖代碼”的锭硼。但是第一個線程進入“加鎖代碼”后去加載圖片卻發(fā)現(xiàn)當(dāng)前并沒有任何圖片房资,因此它只能等待。一旦調(diào)用了NSCondition的wait方法后其他線程就可以繼續(xù)進入“加鎖代碼”(注意檀头,這一點和前面說的NSLock轰异、@synchronized等是不同的,使用NSLock暑始、@synchronized等進行加鎖后無論什么情況下搭独,只要沒有解鎖其他線程就無法進入“加鎖代碼”),同時第一個線程處于等待隊列中(此時并未解鎖)蒋荚。第二個線程進來之后同第一線程一樣戳稽,發(fā)現(xiàn)沒有圖片就進入等待狀態(tài)馆蠕,然后第三個線程進入期升。。互躬。如此反復(fù)播赁,直到第十五個線程也處于等待。此時點擊“創(chuàng)建圖片”后會執(zhí)行createImageName方法吼渡,這是一個生產(chǎn)者容为,它會創(chuàng)建一個圖片鏈接放到imageNames中,然后通過調(diào)用NSCondition的signal方法就會在條件等待隊列中選擇一個線程(該線程會任意選取寺酪,假設(shè)為線程A)開啟坎背,那么此時這個線程就會繼續(xù)執(zhí)行。在上面代碼中寄雀,wati方法之后會繼續(xù)執(zhí)行圖片加載方法得滤,那么此時線程A啟動之后繼續(xù)執(zhí)行圖片加載方法,當(dāng)然此時可以成功加載圖片盒犹。加載完圖片之后線程A就會釋放鎖懂更,整個線程任務(wù)完成眨业。此時再次點擊”創(chuàng)建圖片“按鈕重復(fù)前面的步驟加載其他圖片。
詳情:http://www.cnblogs.com/kenshincui/p/3983982.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沮协,一起剝皮案震驚了整個濱河市龄捡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慷暂,老刑警劉巖聘殖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異行瑞,居然都是意外死亡就斤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門蘑辑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洋机,“玉大人,你說我怎么就攤上這事洋魂”疗欤” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵副砍,是天一觀的道長衔肢。 經(jīng)常有香客問我,道長豁翎,這世上最難降的妖魔是什么角骤? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮心剥,結(jié)果婚禮上邦尊,老公的妹妹穿的比我還像新娘。我一直安慰自己优烧,他們只是感情好蝉揍,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著畦娄,像睡著了一般又沾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上熙卡,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天杖刷,我揣著相機與錄音,去河邊找鬼驳癌。 笑死滑燃,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喂柒。 我是一名探鬼主播不瓶,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼禾嫉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蚊丐?” 一聲冷哼從身側(cè)響起熙参,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎麦备,沒想到半個月后孽椰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡凛篙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年黍匾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呛梆。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡锐涯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出填物,到底是詐尸還是另有隱情纹腌,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布滞磺,位于F島的核電站升薯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏击困。R本人自食惡果不足惜涎劈,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阅茶。 院中可真熱鬧蛛枚,春花似錦、人聲如沸目派。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽企蹭。三九已至,卻和暖如春智末,著一層夾襖步出監(jiān)牢的瞬間谅摄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工系馆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留送漠,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓由蘑,卻偏偏與公主長得像闽寡,于是被迫代替她去往敵國和親代兵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

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

  • 接上文iOS多線程--并行開發(fā)一 4爷狈、線程同步 說到多線程就不得不提多線程中的鎖機制植影,多線程操作過程中往往多個線程...
    John_LS閱讀 772評論 1 5
  • demo下載 建議一邊看文章,一邊看代碼涎永。 聲明:關(guān)于性能的分析是基于我的測試代碼來的思币,我也看到和網(wǎng)上很多測試結(jié)果...
    炸街程序猿閱讀 797評論 0 2
  • 線程安全是怎么產(chǎn)生的 常見比如線程內(nèi)操作了一個線程外的非線程安全變量,這個時候一定要考慮線程安全和同步羡微。 - (v...
    幽城88閱讀 665評論 0 0
  • 前言 說到多線程同步問題就不得不提多線程中的鎖機制妈倔,多線程操作過程中往往多個線程是并發(fā)執(zhí)行的博投,同一個資源可能被多個...
    進無盡閱讀 1,248評論 0 12
  • 文/ 檐鈴化語 每當(dāng)聞到清明的氣息,我的靈魂就開始出竅盯蝴,不受控制的伴著思念朝著牧童手指的方向贬堵,一路狂奔,直達我的故...
    檐鈴化語閱讀 472評論 4 9