在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];
}