iOS多線程 GCD NSoperation NSThread

iOS中 多線程技術有三種 NSThread  GCD NSOperation 這三種方式的抽象層度依次有低到高 ,抽象層度越高 也就用着越方便 也是蘋果官方推薦的 下面我們依次介紹三種多線程技術

1.NSThread 

   優點:有點量級比較輕

    缺點:需要我們自己去管理線程的整個生命週期,使用起來比較麻煩

    在開發工程中 這個方式我們很少用到 

2.GCD  全稱(Grand Central Dispatch) 

   GCD 是一種完全用C語言編寫的iOS多線程技術



介紹完三種多線程技術,下面我們就看一下它們是真麼用的吧

NSThread:這種多線程技術我們在開發過程中很少使用,所及這裏不做過多介紹,簡單的介紹一下

@interface HMViewController ()
- (IBAction)btnClick;

@end

@implementation HMViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
}

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


- (IBAction)btnClick {
    // 1.獲得當前的線程
    NSThread *current = [NSThread currentThread];
    NSLog(@"btnClick---%@", current);
    
//    NSThread *main = [NSThread mainThread];
//    NSLog(@"btnClick---%@", main);
    
    // 2.執行一些耗時操作 : 創建一條子線程
    [self threadCreate];
}

- (void)run:(NSString *)param
{
    NSThread *current = [NSThread currentThread];
    
    for (int i = 0; i<10000; i++) {
        NSLog(@"%@----run---%@", current, param);
    }
}

/**
 * NSThread的創建方式
 * 隱式創建線程, 並且直接(自動)啓動
 */
- (void)threadCreate3
{
    // 在後臺線程中執行 === 在子線程中執行
    [self performSelectorInBackground:@selector(run:) withObject:@"abc參數"];
}

/**
 * NSThread的創建方式
 * 創建完線程直接(自動)啓動
 */
- (void)threadCreate2
{
    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是參數"];
}

/**
 * NSThread的創建方式
 * 1> 先創建初始化線程
 * 2> start開啓線程
 */
- (void)threadCreate
{
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"哈哈"];
    thread.name = @"線程A";
    // 開啓線程
    [thread start];
    
    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"哈哈"];
    thread2.name = @"線程B";
    // 開啓線程
    [thread2 start];
}


GCD

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *leftImage;

@property (weak, nonatomic) IBOutlet UIImageView *rightImage;
@property (weak, nonatomic) IBOutlet UIImageView *bigImage;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
 
    
    
    
    //創建一個同步隊列  同步隊列會按照先進先出的方法一個一個的串發執行任務
    //第一個參數是隊列的名稱,給創建的同步對列添加一個名稱
    //注意這裏是c語言語法不能用 “@”
    //第二個參數是對列的屬性,一般填寫NULL
    dispatch_queue_t queue = dispatch_queue_create("同步隊列", NULL) ;
    dispatch_queue_t queue2 = dispatch_queue_create("同步隊列", NULL) ;
    
    //獲取主線程,主線程是一個同步隊列
    //特別注意,不能用串發執行的方法往主線程中添加任務
    //        dispatch_queue_t queue5 = dispatch_get_main_queue() ;
    //創建一個異步隊列  異步隊列會吧所有任務取出同步執行
    //第一個參數是隊列的優先級 是給隊列設置優先級
    /**
     DISPATCH_QUEUE_PRIORITY_HIGH         較高的優先級        最先執行
     DISPATCH_QUEUE_PRIORITY_DEFAULT      系統默認的優先級     優先級第二
     DISPATCH_QUEUE_PRIORITY_LOW          較低的優先級        優先級第三
     DISPATCH_QUEUE_PRIORITY_BACKGROUND   這個優先級最低     最後執行
     */
    dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)  ;
    dispatch_queue_t queue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)  ;
    
    //sync 串行執行 不允許創建線程
    //async 併發執行,允許創建線程
    //第一個參數是隊列的名稱
    //第二個參數是要添加到隊列中的事情
    //將任務按照串行執行的方法一個一個的添加到同步對列queue中去, 不會創建線程,切任務是一個一個的執行
    dispatch_sync(queue, ^{
        NSLog(@"111111111111111%@",[NSThread currentThread]) ;
    }) ;
    dispatch_sync(queue, ^{
        NSLog(@"555555555555555%@",[NSThread currentThread]) ;
    }) ;
    //將任務按照串行執行的方法一個一個的添加到異步對列queue1中去, 不會創建線程,任務是多個一起執行
    dispatch_sync(queue1, ^{
        NSLog(@"22222222222222%@",[NSThread currentThread]) ;
    }) ;
    //將任務按照併發執行行的方法一個一個的添加到同步對列queue中去, 會創建一個線程,多個任務會在這個創建的線程內一個一個的執行
    dispatch_async(queue2, ^{
        NSLog(@"33333333333333%@",[NSThread currentThread]) ;
    }) ;
    //將任務按照併發執行行的方法一個一個的添加到異步對列queue中去, 會創建多個線程,多個任務會在這個創建的線程內的執行一起執行
    dispatch_async(queue3, ^{
        NSLog(@"44444444444444444%@",[NSThread currentThread]) ;
    }) ;

    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 *NSEC_PER_SEC));
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@",[NSThread currentThread] ) ;
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"%@",[NSThread currentThread] ) ;

    });
    
    dispatch_group_t group = dispatch_group_create() ;
    dispatch_group_async(group, queue, ^{
        NSLog(@"6666666666666group") ;
    }) ;
dispatch_group_async(group, queue1, ^{
     NSLog(@"11111111111111group") ;
}) ;
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"group") ;
    }) ;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"啊哈哈哈哈") ;
    });
    dispatch_group_t group = dispatch_group_create() ;
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ;
    dispatch_queue_t queue1 = dispatch_queue_create("123", NULL) ;
    
    __block  UIImage * image = nil ;
    __block  UIImage * image1 = nil ;
   dispatch_group_async(group, queue, ^{
       NSString * string = @"http://f.hiphotos.baidu.com/image/pic/item/0eb30f2442a7d933fdd1619ba94bd11372f001d8.jpg" ;
       NSURL * URL = [NSURL URLWithString:string] ;
       NSData * data = [NSData dataWithContentsOfURL:URL] ;
       image = [UIImage imageWithData:data] ;
       NSLog(@"image1") ;
   }) ;
  
   dispatch_group_async(group, queue, ^{
       NSString * string = @"http://h.hiphotos.baidu.com/image/pic/item/d058ccbf6c81800a7e38fce4b53533fa838b477e.jpg" ;
       NSURL * URL = [NSURL URLWithString:string] ;
       NSData * data = [NSData dataWithContentsOfURL:URL] ;
       image1 = [UIImage imageWithData:data] ;
       NSLog(@"imag2") ;
   });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        self.leftImage.image = image ;
        self.rightImage.image = image1 ;
    }) ;
    
}

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

第二個例子

@interface HMViewController ()
@property (nonatomic, strong) NSThread *thread;
@end

@implementation HMViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil];
    self.thread.name = @"線程A";
}

- (void)test
{
    NSLog(@"test - 開始 - %@", [NSThread currentThread].name);
    
//    [NSThread sleepForTimeInterval:5]; // 阻塞狀態
    
//    NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5.0];
//    [NSThread sleepUntilDate:date];
    
    for (int i = 0; i<1000; i++) {
        NSLog(@"test - %d - %@", i, [NSThread currentThread].name);
        
        if (i == 50) {
            [NSThread exit];
        }
    }
    
    NSLog(@"test - 結束 - %@", [NSThread currentThread].name);
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 開啓線程
    [self.thread start];
}



NSOperation 是一種基於GCD開發的技術,它突出了任務的概念,弱化線程的概念。起始NSOperation並不能進行進行多線程任務,而是他的子類方法進行的 一般情況下NSOperation需要與NSOperationQueue結合起來使用 NSOperation是通過子類NSBlockOperation、NSInvocationOperation和自定義的NSOperation的子類來實現,這裏只對前兩個做介紹,因爲前兩個基本上能滿足我們開發的需求

NSOperation:

創建一個任務

   //創建操作對象
    NSInvocationOperation * operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil] ;
   //添加到任務隊列中去
    NSOperationQueue * queue = [[NSOperationQueue alloc]init] ;
    //把任務添加到任務隊列中去
    //注意這裏需要天加到任務隊列中去,要不然需要手動調用  [operation start] 方法開啓線程,並且是不會開闢新線程的,無論起了多掃個任務
    [queue addOperation:operation] ;
創建一個NSBlockOperation任務
//創建block任務 默認情況下是不會創建線程
    NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"-----下載圖片1-------%@",[NSThread currentThread]) ;
}] ;
    // 注意  blockOperation 如果添加多任務即便是不放在 任務隊列 中仍然開闢線程
    [operation addExecutionBlock:^{
        NSLog(@"-----下載圖片2-------%@",[NSThread currentThread]) ;
    }] ;
    [operation addExecutionBlock:^{
        NSLog(@"-----下載圖片3-------%@",[NSThread currentThread]) ;
        
    }] ;
    [operation addExecutionBlock:^{
        NSLog(@"-----下載圖片4-------%@",[NSThread currentThread]) ;
    }] ;
    //開始線程
    [operation start] ;
這樣就開闢了三條線程 ,如果添加到OperationQueue,所有的任務都是在子線程內實現的,不在主線程中

NSOperationQueue的一些設置

設置最大的開啓線程的數量

 //設置最大併發線程的數量(不包括主線程),如果設置爲3 包括主線程在內爲4個
    queue.maxConcurrentOperationCount = 3 ;//5以內

取消所有線程

//取消所有的線程
    [queue cancelAllOperations] ;
暫停和開啓所有線程
//暫停線程 YES 表示暫停
    [queue setSuspended:YES] ;
    //恢復線程  NO表示繼續
    [queue setSuspended:NO] ;
取消單個線程
  [operation1 cancel] ;

設置現成的優先級,優先級越高,執行的可能性越大,被執行的次數越多
//設置優先級
    /*
     NSOperationQueuePriorityVeryLow = -8L,
     NSOperationQueuePriorityLow = -4L,
     NSOperationQueuePriorityNormal = 0,
     NSOperationQueuePriorityHigh = 4,
     NSOperationQueuePriorityVeryHigh = 8
     */
    [operation1 setQueuePriority:NSOperationQueuePriorityHigh] ;
設置線程的依賴關係 
//操作依賴  operation2 做完後才能做 operation1
    //可以在不同隊列中添加依賴,但是不能相互依賴
    [operation1 addDependency:operation2] ;
第三個例子

@interface HMViewController ()
/** 剩餘票數 */
@property (nonatomic, assign) int leftTicketsCount;
@property (nonatomic, strong) NSThread *thread0;
@property (nonatomic, strong) NSThread *thread1;
@property (nonatomic, strong) NSThread *thread2;
@end

@implementation HMViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 默認有100張
    self.leftTicketsCount = 100;
    
    // 開啓多條線程同時賣票
    self.thread0 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread0.name = @"售票員 A";
//    self.thread0.threadPriority = 0.0;
    
    self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread1.name = @"售票員 B";
//    self.thread1.threadPriority = 1.0;
    
    self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread2.name = @"售票員 C";
//    self.thread2.threadPriority = 0.0;
}

/**
 * 賣票
 */
- (void)saleTicket
{
    while (1) {
        @synchronized(self) { // 加鎖(只能用一把鎖)
            // 1.先檢查票數
            int count = self.leftTicketsCount;
            if (count > 0) {
                // 暫停
//                [NSThread sleepForTimeInterval:0.0002];
                
                // 2.票數 - 1
                self.leftTicketsCount = count - 1;
                
                NSThread *current = [NSThread currentThread];
                NSLog(@"%@ 賣了一張票, 剩餘%d張票", current.name, self.leftTicketsCount);
            } else {
                // 退出線程
                [NSThread exit];
            }
        } // 解鎖
    }
}

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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章