NSThread、NSOperation、GCD 总结:

  1. 无论使用哪种方法进行多线程开发,每个线程启动后并不一定立即执行相应的操作,具体什么时候由系统调度(CPU 空闲时就会执行)

  2. 更新 UI 应该在主线程(UI 线程)中进行,并且推荐使用异步调用,避免造成线程阻塞,常用的方法如下:

    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL) wait; //方法传递主线程[NSThread mainThread])

    [NSOperationQueue mainQueue] addOperationWithBlock:

    dispatch_async(dispatch_get_main_queue(), ^{}) //在主线程中,这里改用同步调用 dispatch_sync 的话就会造成线程阻塞情况

  3. NSThread 适合轻量级多线程开发,控制线程顺序比较难,同时线程总数无法控制(每次创建并不能重用之前的线程,只能创建一个新的线程)

  4. 对于简单的多线程开发建议使用 NSObject 的扩展方法完成,而不必使用 NSThread

  5. 可以使用 NSThread 的 currentThread 方法取得当前线程,使用 sleepForTimeInterval: 方法让当前线程挂起

  6. NSOperation 进行多线程开发可以控制线程总数及线程依赖关系

  7. 创建一个 NSOperation 不应该直接调用 start 方法(如果直接 start 则会在主线程中调用)而是应该放到 NSOperationQueue 中启动

  8. 相比 NSInvocationOperation 推荐使用 NSBlockOperation,代码简单,同时由于闭包性使他没有传参问题

  9. NSOperation 是对 GCD 面向对象的 OC 封装。而 GCD 基于 C 语言开发,效率更高。建议如果任务之间有依赖关系或者想要监听任务完成状态的情况下优先选择 NSOperation,否则使用 GCD

  10. 在 GCD 中串行队列中的任务被安排到一个单一线程执行(不是主线程),可以方便地控制执行顺序;并发队列在多个线程中执行(前提是使用异步方法),顺序控制相对复杂,但是更高效

  11. 在 GCD 中一个操作是多线程还是单线程执行,取决于当前队列类型和执行方法。只有队列类型为并行队列并且使用异步方法执行时才能在多个线程中执行(如果是并行队列使用同步方法调用则会在主线程中执行)

效果如下:

ViewController.h

 #import <UIKit/UIKit.h>

 @interface ViewController : UITableViewController
@property (copy, nonatomic) NSArray *arrSampleName; - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName; @end

ViewController.m

 #import "ViewController.h"
#import "FirstSampleViewController.h"
#import "SecondSampleViewController.h" @interface ViewController ()
- (void)layoutUI;
@end @implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad]; [self layoutUI];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName {
if (self = [super initWithStyle:UITableViewStyleGrouped]) {
self.navigationItem.title = @"多线程开发之三 GCD";
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回首页" style:UIBarButtonItemStylePlain target:nil action:nil]; _arrSampleName = arrSampleName;
}
return self;
} - (void)layoutUI {
} #pragma mark - UITableViewController相关方法重写
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 0.1;
} - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_arrSampleName count];
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = _arrSampleName[indexPath.row];
return cell;
} - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case : {
FirstSampleViewController *firstSampleVC = [FirstSampleViewController new];
[self.navigationController pushViewController:firstSampleVC animated:YES];
break;
}
case : {
SecondSampleViewController *secondSampleVC = [SecondSampleViewController new];
[self.navigationController pushViewController:secondSampleVC animated:YES];
break;
}
default:
break;
}
} @end

UIImage+RescaleImage.h

 #import <UIKit/UIKit.h>

 @interface UIImage (RescaleImage)
/**
* 根据宽高大小,获取对应的缩放图片
*
* @param size 宽高大小
*
* @return 对应的缩放图片
*/
- (UIImage *)rescaleImageToSize:(CGSize)size; @end 

UIImage+RescaleImage.m

 #import "UIImage+RescaleImage.h"

 @implementation UIImage (RescaleImage)

 - (UIImage *)rescaleImageToSize:(CGSize)size {
CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height); UIGraphicsBeginImageContext(rect.size);
[self drawInRect:rect];
UIImage *imgScale = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext(); return imgScale;
} @end

Common.h

 #import <Foundation/Foundation.h>

 @interface Common : NSObject
+ (NSURL *)randomImageURL; @end 

Common.m

 #import "Common.h"

 static NSArray *arrPartOfURL = nil;
@implementation Common + (NSURL *)randomImageURL {
if (arrPartOfURL == nil) {
arrPartOfURL = @[
@"chronograph",
@"color",
@"modular",
@"utility",
@"mickey_mouse",
@"simple",
@"motion",
@"solar",
@"astronomy",
@"timer",
@"alarm",
@"world_clock"
];
} NSUInteger randomVal = (arc4random() % ); //0-11的随机数
NSString *imageURLStr = [NSString stringWithFormat:@"http://images.apple.com/v/watch/e/timekeeping/images/%@_large.jpg", arrPartOfURL[randomVal]];
return [NSURL URLWithString:imageURLStr];
} @end 

FirstSampleViewController.h

 #import <UIKit/UIKit.h>

 @interface FirstSampleViewController : UIViewController
@property (assign, nonatomic) CGSize rescaleImageSize; @property (strong, nonatomic) IBOutlet UIImageView *imgV;
@property (strong, nonatomic) IBOutlet UIButton *btnLoadImage; @end 

FirstSampleViewController.m

 #import "FirstSampleViewController.h"
#import "UIImage+RescaleImage.h"
#import "Common.h" @interface FirstSampleViewController ()
- (void)layoutUI;
- (void)updateImage:(NSData *)imageData;
@end @implementation FirstSampleViewController - (void)viewDidLoad {
[super viewDidLoad]; [self layoutUI];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} - (void)dealloc {
_imgV.image = nil;
} - (void)layoutUI {
self.view.backgroundColor = [UIColor colorWithWhite:0.957 alpha:1.000]; CGFloat width = [[UIScreen mainScreen] bounds].size.width; //bounds 返回整个屏幕大小;applicationFrame 返回去除状态栏后的屏幕大小
CGFloat height = width * 243.0 / 221.0;
const CGFloat percentVal = 3.0 / 4.0; //以屏幕宽度的3/4比例显示
_rescaleImageSize = CGSizeMake(width * percentVal, height * percentVal); //NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"];
//_imgV.image = [UIImage imageWithContentsOfFile:path]; _btnLoadImage.tintColor = [UIColor darkGrayColor];
_btnLoadImage.layer.masksToBounds = YES;
_btnLoadImage.layer.cornerRadius = 10.0;
_btnLoadImage.layer.borderColor = [UIColor grayColor].CGColor;
_btnLoadImage.layer.borderWidth = 1.0;
} - (void)updateImage:(NSData *)imageData {
UIImage *img = [UIImage imageWithData:imageData];
_imgV.image = [img rescaleImageToSize:_rescaleImageSize];
} - (IBAction)loadImage:(id)sender {
/*
GCD(Grand Central Dispatch):中央调度
相比 NSThread 和 NSOperation 来说,他的显著优点是:对于多核运算更加有效
1、GCD 的一个重要概念是队列,他的核心理念:将多个任务添加到 dispatch queue(调度队列)中,系统会为我们管理这些 dispath queue,为我们在多个线程上分配并执行任务,我们不需要直接启动和管理后台线程。
2、系统提供了许多预定义的 dispatch queue,包括可以保证始终在主线程上执行工作的 dispatch queue。也可以创建自己的dispatch queue,而且可以创建任意多个。GCD的 dispatch queue 严格遵循 FIFO(先进先出)原则,添加到 dispatch queue 的任务将始终按照加入 dispatch queue 的顺序启动。
3、dispatch queue按先进先出的顺序,串行或并发地执行任务
1> serial dispatch queue(串行调度队列):一次只能执行一个任务,当前任务完成后才开始出列并启动下一个任务
2> concurrent dispatch queue(并发调度队列):则尽可能多地启动任务并发执行 同步和异步决定了要不要开启新的线程
同步:在当前线程中执行任务,不具备开启新线程的能力(如果当前线程是主线程的话,会造成线程阻塞,一般比较少用)
异步:在新的线程中执行任务,具备开启新线程的能力 串行和并发决定了任务的执行方式
串行:一个任务执行完毕后,再执行下一个任务
并发:多个任务并发(同时)执行
*/ /*
系统给每个应用提供了四个全局并发队列,整个应用内全局共享,他们的区别是通过「全局并发队列优先级」;优先级越高,队列越先被列入执行计划中
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
*/ //全局并发队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ); //自定义串行队列;第二个参数为 NULL 或 DISPATCH_QUEUE_SERIAL 表示串行队列,而为 DISPATCH_QUEUE_CONCURRENT 表示并发队列,但一般用 dispatch_get_global_queue 方法获取并发队列
//dispatch_queue_t customSerialQueue = dispatch_queue_create("Custom Queue", NULL); //主队列;属于串行队列
dispatch_queue_t mainQueue = dispatch_get_main_queue(); //方法一:自定义串行队列同步:不会开启新线程、串行执行任务
// dispatch_sync(customSerialQueue, ^{
// NSLog(@"自定义串行队列同步,线程:%@", [NSThread currentThread]); //所在的线程,这里就是在主线程
// NSURL *url = [Common randomImageURL];
// NSData *data = [NSData dataWithContentsOfURL:url];
//
// dispatch_async(mainQueue, ^{ //主队列异步(同步也一样):不会开启新线程、串行执行任务
// NSLog(@"主队列异步(同步也一样),线程:%@", [NSThread currentThread]); //主队列所对应的主线程
// [self updateImage:data];
// });
// }); //方法二:自定义串行队列异步:开启新线程、串行执行任务
// dispatch_async(customSerialQueue, ^{
// NSLog(@"自定义串行队列异步,线程:%@", [NSThread currentThread]); //开启的新线程
// NSURL *url = [Common randomImageURL];
// NSData *data = [NSData dataWithContentsOfURL:url];
//
// dispatch_async(mainQueue, ^{ //主队列异步(同步也一样):不会开启新线程、串行执行任务
// NSLog(@"主队列异步(同步也一样),线程:%@", [NSThread currentThread]); //主队列所对应的主线程
// [self updateImage:data];
// });
// }); //方法三:全局并发队列同步:不会开启新线程、串行执行任务
// dispatch_sync(globalQueue, ^{
// NSLog(@"全局并发队列同步,线程:%@", [NSThread currentThread]); //所在的线程,这里就是在主线程
// NSURL *url = [Common randomImageURL];
// NSData *data = [NSData dataWithContentsOfURL:url];
//
// dispatch_async(mainQueue, ^{ //主队列异步(同步也一样):不会开启新线程、串行执行任务
// NSLog(@"主队列异步(同步也一样),线程:%@", [NSThread currentThread]); //主队列所对应的主线程
// [self updateImage:data];
// });
// }); //方法四:全局并发队列异步:开启新线程、并发执行任务
dispatch_async(globalQueue, ^{
NSLog(@"全局并发队列异步,线程:%@", [NSThread currentThread]); //开启的新线程
NSURL *url = [Common randomImageURL];
NSData *data = [NSData dataWithContentsOfURL:url]; dispatch_async(mainQueue, ^{ //主队列异步(同步也一样):不会开启新线程、串行执行任务
NSLog(@"主队列异步(同步也一样),线程:%@", [NSThread currentThread]); //主队列所对应的主线程
[self updateImage:data];
});
});
} @end 

FirstSampleViewController.xib

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FirstSampleViewController">
<connections>
<outlet property="btnLoadImage" destination="sLs-f1-Gzc" id="kX8-J0-v0V"/>
<outlet property="imgV" destination="4Qp-uk-KAb" id="RM3-Ha-glh"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="PictureNo.png" translatesAutoresizingMaskIntoConstraints="NO" id="4Qp-uk-KAb">
<rect key="frame" x="205" y="225" width="190" height="150"/>
<constraints>
<constraint firstAttribute="height" constant="150" id="SIp-Wd-idU"/>
<constraint firstAttribute="height" constant="150" id="VwM-i1-atB"/>
<constraint firstAttribute="width" constant="190" id="mUh-Bu-tUd"/>
<constraint firstAttribute="width" constant="190" id="mdJ-1c-QFa"/>
<constraint firstAttribute="width" constant="190" id="sVS-bU-Ty9"/>
<constraint firstAttribute="height" constant="150" id="uMG-oN-J56"/>
<constraint firstAttribute="height" constant="150" id="vws-Qw-UrB"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="SIp-Wd-idU"/>
<exclude reference="VwM-i1-atB"/>
<exclude reference="mUh-Bu-tUd"/>
<exclude reference="mdJ-1c-QFa"/>
<exclude reference="sVS-bU-Ty9"/>
<exclude reference="uMG-oN-J56"/>
<exclude reference="vws-Qw-UrB"/>
</mask>
</variation>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sLs-f1-Gzc">
<rect key="frame" x="230" y="500" width="140" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="140" id="1jv-9K-mdH"/>
<constraint firstAttribute="height" constant="50" id="Q2w-vR-9ac"/>
</constraints>
<state key="normal" title="加载网络图片">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="loadImage:" destination="-1" eventType="touchUpInside" id="fdy-Ln-5oS"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="4Qp-uk-KAb" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="205" id="2a2-mS-WFa"/>
<constraint firstItem="sLs-f1-Gzc" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="ES4-wl-RBz"/>
<constraint firstItem="4Qp-uk-KAb" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" id="MUJ-WA-sUf"/>
<constraint firstItem="4Qp-uk-KAb" firstAttribute="centerX" secondItem="sLs-f1-Gzc" secondAttribute="centerX" id="Q8a-1k-DzJ"/>
<constraint firstAttribute="bottom" secondItem="4Qp-uk-KAb" secondAttribute="bottom" constant="71" id="V0a-9y-Dwa"/>
<constraint firstAttribute="bottom" secondItem="sLs-f1-Gzc" secondAttribute="bottom" constant="50" id="VMG-CV-eeq"/>
<constraint firstItem="4Qp-uk-KAb" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="-71" id="gqW-Wq-4Zv"/>
<constraint firstItem="sLs-f1-Gzc" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="kNf-6d-EJ8"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="2a2-mS-WFa"/>
<exclude reference="V0a-9y-Dwa"/>
<exclude reference="gqW-Wq-4Zv"/>
<exclude reference="ES4-wl-RBz"/>
</mask>
</variation>
</view>
</objects>
<resources>
<image name="PictureNo.png" width="190" height="150"/>
</resources>
</document> 

SecondSampleViewController.h

 #import <UIKit/UIKit.h>

 @interface SecondSampleViewController : UIViewController
@property (assign, nonatomic) CGSize rescaleImageSize;
@property (strong, nonatomic) NSMutableArray *mArrImageView; @property (strong, nonatomic) IBOutlet UIButton *btnLoadImage; @end 

SecondSampleViewController.m

 #import "SecondSampleViewController.h"
#import "UIImage+RescaleImage.h"
#import "Common.h" #define kRowCount 4
#define kColumnCount 3
#define kCellSpacing 10.0 @interface SecondSampleViewController ()
- (void)layoutUI;
- (void)updateImage:(NSData *)imageData withImageIndex:(NSInteger)imageIndex;
-(NSData *)requestData:(NSInteger)imageIndex;
- (void)loadImageFromNetwork:(NSInteger)imageIndex;
- (void)loadImageByDispatchAsync;
- (void)loadImageByDispatchApply;
- (void)loadImageByDispatchGroupAsync;
- (void)additionalInfo;
@end @implementation SecondSampleViewController - (void)viewDidLoad {
[super viewDidLoad]; [self layoutUI];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} - (void)dealloc {
_mArrImageView = nil;
} - (void)layoutUI {
self.view.backgroundColor = [UIColor colorWithWhite:0.957 alpha:1.000]; CGFloat width = ([[UIScreen mainScreen] bounds].size.width - ((kColumnCount + ) * kCellSpacing)) / kColumnCount;
_rescaleImageSize = CGSizeMake(width, width); CGFloat heightOfStatusAndNav = 20.0 + 44.0;
NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"];
UIImage *img = [UIImage imageWithContentsOfFile:path];
_mArrImageView = [NSMutableArray arrayWithCapacity:kRowCount * kColumnCount];
//初始化多个图片视图
for (NSUInteger i=; i<kRowCount; i++) {
for (NSUInteger j=; j<kColumnCount; j++) {
UIImageView *imgV = [[UIImageView alloc] initWithFrame:
CGRectMake(_rescaleImageSize.width * j + kCellSpacing * (j+),
_rescaleImageSize.height * i + kCellSpacing * (i+) + heightOfStatusAndNav,
_rescaleImageSize.width,
_rescaleImageSize.height)];
imgV.image = img;
[self.view addSubview:imgV];
[_mArrImageView addObject:imgV];
}
} _btnLoadImage.tintColor = [UIColor darkGrayColor];
_btnLoadImage.layer.masksToBounds = YES;
_btnLoadImage.layer.cornerRadius = 10.0;
_btnLoadImage.layer.borderColor = [UIColor grayColor].CGColor;
_btnLoadImage.layer.borderWidth = 1.0;
} - (void)updateImage:(NSData *)imageData withImageIndex:(NSInteger)imageIndex {
UIImage *img = [UIImage imageWithData:imageData];
UIImageView *imgVCurrent = _mArrImageView[imageIndex];
imgVCurrent.image = [img rescaleImageToSize:_rescaleImageSize];
} -(NSData *)requestData:(NSInteger)imageIndex {
//对于多线程操作,建议把线程操作放到 @autoreleasepool 中
@autoreleasepool {
NSURL *url = [Common randomImageURL];
NSData *data = [NSData dataWithContentsOfURL:url];
return data;
}
} - (void)loadImageFromNetwork:(NSInteger)imageIndex {
NSData *data = [self requestData:imageIndex]; NSLog(@"Current thread:%@, imageIndex:%ld", [NSThread currentThread], (long)imageIndex); //dispatch_async、dispatch_apply、dispatch_group_async 的全局并发队列,会开启多个新线程去执行多个任务;按系统管理,也可能存在一个线程执行两个任务的情况 dispatch_async(dispatch_get_main_queue(), ^{ //主队列异步(同步也一样):不会开启新线程、串行执行任务
NSLog(@"主队列异步(同步也一样),线程:%@", [NSThread currentThread]); //主队列所对应的主线程
[self updateImage:data withImageIndex:imageIndex];
});
} #pragma mark - 三种方法实现多线程并发执行多个任务,并且优先加载最后一张图片
- (void)loadImageByDispatchAsync {
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ); //全局并发队列 dispatch_async(globalQueue, ^{
NSInteger len = kRowCount * kColumnCount - ; NSLog(@"全局并发队列异步,线程:%@", [NSThread currentThread]); //开启的新线程
[self loadImageFromNetwork:len]; //优先加载最后一张图片 for (NSUInteger i=; i<len; i++) {
dispatch_async(globalQueue, ^{
[self loadImageFromNetwork:i];
});
}
});
} - (void)loadImageByDispatchApply {
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ); //全局并发队列 //使用 dispatch_async,全局并发队列异步:开启新线程、并发执行任务;此时会并发无序地逐个图片加载并显示出来
//PS:如果使用 dispatch_sync,全局并发队列同步:不会开启新线程、串行执行任务;此时会等到全部图片加载完才全部显示出来
dispatch_async(globalQueue, ^{
size_t len = kRowCount * kColumnCount - ; NSLog(@"全局并发队列异步,线程:%@", [NSThread currentThread]); //开启的新线程
[self loadImageFromNetwork:len]; //优先加载最后一张图片 //使用 dispatch_apply 或 dispatch_apply_f 来循环迭代执行任务;前提是迭代的任务相互独立,而且任务执行顺序是无关紧要的;
//他每次循环迭代会将指定的任务提交到 queue,queue 会开启多个新线程(线程会尽量复用,线程更少,系统多线程之间切换调度更快)去执行多个任务,执行顺序是不会像 for 循环那样有序执行;
//但是他像 for 循环一样会在所有任务循环迭代执行完后才返回,会阻塞当前线程,所以只用于并发队列。如果用于串行队列,会造成死锁,无法正常执行
dispatch_apply(len, globalQueue, ^(size_t i) {
[self loadImageFromNetwork:i];
});
});
} - (void)loadImageByDispatchGroupAsync {
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ); //全局并发队列 //使用 dispatch_async,全局并发队列异步:开启新线程、并发执行任务;此时会并发无序地逐个图片加载并显示出来
//PS:如果使用 dispatch_sync,全局并发队列同步:不会开启新线程、串行执行任务;此时会等到全部图片加载完才全部显示出来
dispatch_async(globalQueue, ^{
NSInteger len = kRowCount * kColumnCount - ; NSLog(@"全局并发队列异步,线程:%@", [NSThread currentThread]); //开启的新线程
[self loadImageFromNetwork:len]; //优先加载最后一张图片 dispatch_group_t group = dispatch_group_create(); //自定义组
for (NSUInteger i=; i<len; i++) {
//把一系列任务提交到队列中,并统一关联到自定义组中;这样并发全部执行完毕后,能通过 dispatch_group_notify 进行后续处理
dispatch_group_async(group, globalQueue, ^{
[self loadImageFromNetwork:i];
});
} //自定义组通知;会根据队列类型决定是否开启新线程;如果是并发队列就会开启新线程(实际上是:复用自定义组执行最后一个任务的对应线程),否则是串行队列就不会
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"自定义组通知,线程:%@", [NSThread currentThread]);
NSLog(@"全部的 dispatch_group_async 任务执行完毕后,做一些事情,只执行一次");
});
});
} - (void)additionalInfo {
//dispatch_suspend(queue); //挂起队列方法;当前正在执行的任务不会停下来,只是不再继续执行未启动的任务
//dispatch_resume(queue); //恢复队列方法;确保他跟 dispatch_suspend 成对调用 //单次执行任务方法;方法是线程安全的
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"dispatch_once:单次执行任务;只会执行一次,重复调用方法也不会重复执行(可用于应用程序中初始化全局数据的情况,例如:单例模式)");
}); //类似 - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay 的延时执行方法
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"dispatch_after:延时执行任务,这里2.0秒后做一些事情");
});
} - (IBAction)loadImage:(id)sender {
//三种方法实现多线程并发执行多个任务,并且优先加载最后一张图片
//方法一:dispatch_async
//[self loadImageByDispatchAsync]; //方法二:dispatch_apply
//[self loadImageByDispatchApply]; //方法三:dispatch_group_async
[self loadImageByDispatchGroupAsync]; //附加信息
[self additionalInfo];
} @end 

SecondSampleViewController.xib

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="SecondSampleViewController">
<connections>
<outlet property="btnLoadImage" destination="F5h-ZI-gGL" id="I40-e2-bAa"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F5h-ZI-gGL">
<rect key="frame" x="230" y="530" width="140" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="HWd-Xc-Wk7"/>
<constraint firstAttribute="width" constant="140" id="vrH-qE-PNK"/>
</constraints>
<state key="normal" title="加载网络图片">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="loadImage:" destination="-1" eventType="touchUpInside" id="Hgw-q8-lHy"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="F5h-ZI-gGL" secondAttribute="bottom" constant="20" id="jPY-fY-9XJ"/>
<constraint firstAttribute="centerX" secondItem="F5h-ZI-gGL" secondAttribute="centerX" id="rH1-sV-pST"/>
</constraints>
</view>
</objects>
</document> 

AppDelegate.h

 #import <UIKit/UIKit.h>

 @interface AppDelegate : UIResponder <UIApplicationDelegate>

 @property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UINavigationController *navigationController; @end 

AppDelegate.m

 #import "AppDelegate.h"
#import "ViewController.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController *viewController = [[ViewController alloc] initWithSampleNameArray:@[@"请求单张网络图片(解决线程阻塞)", @"请求多张网络图片(多个线程并发)"]];
_navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
_window.rootViewController = _navigationController;
//[_window addSubview:_navigationController.view]; //当_window.rootViewController关联时,这一句可有可无
[_window makeKeyAndVisible];
return YES;
} - (void)applicationWillResignActive:(UIApplication *)application {
} - (void)applicationDidEnterBackground:(UIApplication *)application {
} - (void)applicationWillEnterForeground:(UIApplication *)application {
} - (void)applicationDidBecomeActive:(UIApplication *)application {
} - (void)applicationWillTerminate:(UIApplication *)application {
} @end 

输出结果:

方法三:dispatch_group_async

 -- ::44.168 GCDDemo[:] 全局并发队列异步,线程:<NSThread: 0x7f9511c7a920>{number = , name = (null)}
-- ::44.168 GCDDemo[:] dispatch_once:单次执行任务;只会执行一次,重复调用方法也不会重复执行(可用于应用程序中初始化全局数据的情况,例如:单例模式)
-- ::44.229 GCDDemo[:] Current thread:<NSThread: 0x7f9511c7a920>{number = , name = (null)}, imageIndex:
-- ::44.230 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::44.283 GCDDemo[:] Current thread:<NSThread: 0x7f9511c7a920>{number = , name = (null)}, imageIndex:
-- ::44.283 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::44.301 GCDDemo[:] Current thread:<NSThread: 0x7f9511f2b3f0>{number = , name = (null)}, imageIndex:
-- ::44.301 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::44.516 GCDDemo[:] Current thread:<NSThread: 0x7f9511ef52f0>{number = , name = (null)}, imageIndex:
-- ::44.516 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::44.887 GCDDemo[:] Current thread:<NSThread: 0x7f9511d94230>{number = , name = (null)}, imageIndex:
-- ::44.888 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::44.999 GCDDemo[:] Current thread:<NSThread: 0x7f9511d93d40>{number = , name = (null)}, imageIndex:
-- ::44.999 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::45.152 GCDDemo[:] Current thread:<NSThread: 0x7f9511d78f90>{number = , name = (null)}, imageIndex:
-- ::45.152 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::45.190 GCDDemo[:] Current thread:<NSThread: 0x7f9511ef0340>{number = , name = (null)}, imageIndex:
-- ::45.190 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::45.240 GCDDemo[:] Current thread:<NSThread: 0x7f9511d8e9d0>{number = , name = (null)}, imageIndex:
-- ::45.240 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::45.314 GCDDemo[:] Current thread:<NSThread: 0x7f9511ecd030>{number = , name = (null)}, imageIndex:
-- ::45.314 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::45.443 GCDDemo[:] Current thread:<NSThread: 0x7f9511d8fc50>{number = , name = (null)}, imageIndex:
-- ::45.443 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::45.615 GCDDemo[:] Current thread:<NSThread: 0x7f9511d91740>{number = , name = (null)}, imageIndex:
-- ::45.615 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::45.620 GCDDemo[:] 自定义组通知,线程:<NSThread: 0x7f9511d0a9a0>{number = , name = main}
-- ::45.620 GCDDemo[:] 全部的 dispatch_group_async 任务执行完毕后,做一些事情,只执行一次
-- ::46.360 GCDDemo[:] dispatch_after:延时执行任务,这里2.0秒后做一些事情 

方法一:dispatch_async

 -- ::08.490 GCDDemo[:] 全局并发队列异步,线程:<NSThread: 0x7fc5be00c810>{number = , name = (null)}
-- ::08.490 GCDDemo[:] dispatch_once:单次执行任务;只会执行一次,重复调用方法也不会重复执行(可用于应用程序中初始化全局数据的情况,例如:单例模式)
-- ::08.862 GCDDemo[:] Current thread:<NSThread: 0x7fc5be00c810>{number = , name = (null)}, imageIndex:
-- ::08.862 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::09.062 GCDDemo[:] Current thread:<NSThread: 0x7fc5be05ac90>{number = , name = (null)}, imageIndex:
-- ::09.062 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::09.066 GCDDemo[:] Current thread:<NSThread: 0x7fc5be00c810>{number = , name = (null)}, imageIndex:
-- ::09.067 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::09.323 GCDDemo[:] Current thread:<NSThread: 0x7fc5bbf60040>{number = , name = (null)}, imageIndex:
-- ::09.323 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::09.323 GCDDemo[:] Current thread:<NSThread: 0x7fc5be05bf50>{number = , name = (null)}, imageIndex:
-- ::09.324 GCDDemo[:] Current thread:<NSThread: 0x7fc5bbd05bf0>{number = , name = (null)}, imageIndex:
-- ::09.327 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::09.330 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::09.486 GCDDemo[:] Current thread:<NSThread: 0x7fc5bbd1f880>{number = , name = (null)}, imageIndex:
-- ::09.486 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::10.156 GCDDemo[:] Current thread:<NSThread: 0x7fc5be05ae70>{number = , name = (null)}, imageIndex:
-- ::10.156 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::10.208 GCDDemo[:] Current thread:<NSThread: 0x7fc5bbd26b00>{number = , name = (null)}, imageIndex:
-- ::10.209 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::10.239 GCDDemo[:] Current thread:<NSThread: 0x7fc5bbe9ff30>{number = , name = (null)}, imageIndex:
-- ::10.239 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::10.491 GCDDemo[:] dispatch_after:延时执行任务,这里2.0秒后做一些事情
-- ::10.838 GCDDemo[:] Current thread:<NSThread: 0x7fc5bbea3940>{number = , name = (null)}, imageIndex:
-- ::10.838 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main}
-- ::11.489 GCDDemo[:] Current thread:<NSThread: 0x7fc5bbea4dd0>{number = , name = (null)}, imageIndex:
-- ::11.490 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fc5bbe280d0>{number = , name = main} 

方法二:dispatch_apply

 -- ::50.873 GCDDemo[:] 全局并发队列异步,线程:<NSThread: 0x7fe57149bb60>{number = , name = (null)}
-- ::50.873 GCDDemo[:] dispatch_once:单次执行任务;只会执行一次,重复调用方法也不会重复执行(可用于应用程序中初始化全局数据的情况,例如:单例模式)
-- ::50.973 GCDDemo[:] Current thread:<NSThread: 0x7fe57149bb60>{number = , name = (null)}, imageIndex:
-- ::50.974 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::51.018 GCDDemo[:] Current thread:<NSThread: 0x7fe5714f8490>{number = , name = (null)}, imageIndex:
-- ::51.018 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::51.078 GCDDemo[:] Current thread:<NSThread: 0x7fe5714f8490>{number = , name = (null)}, imageIndex:
-- ::51.078 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::51.087 GCDDemo[:] Current thread:<NSThread: 0x7fe5717bd740>{number = , name = (null)}, imageIndex:
-- ::51.087 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::51.200 GCDDemo[:] Current thread:<NSThread: 0x7fe57149bb60>{number = , name = (null)}, imageIndex:
-- ::51.200 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::51.271 GCDDemo[:] Current thread:<NSThread: 0x7fe5717bd740>{number = , name = (null)}, imageIndex:
-- ::51.271 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::51.628 GCDDemo[:] Current thread:<NSThread: 0x7fe57152d910>{number = , name = (null)}, imageIndex:
-- ::51.628 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::51.701 GCDDemo[:] Current thread:<NSThread: 0x7fe5714f8490>{number = , name = (null)}, imageIndex:
-- ::51.701 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::51.800 GCDDemo[:] Current thread:<NSThread: 0x7fe57149bb60>{number = , name = (null)}, imageIndex:
-- ::51.800 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::52.063 GCDDemo[:] Current thread:<NSThread: 0x7fe57152d910>{number = , name = (null)}, imageIndex:
-- ::52.064 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::52.109 GCDDemo[:] Current thread:<NSThread: 0x7fe5717bd740>{number = , name = (null)}, imageIndex:
-- ::52.110 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::52.295 GCDDemo[:] Current thread:<NSThread: 0x7fe5714f8490>{number = , name = (null)}, imageIndex:
-- ::52.295 GCDDemo[:] 主队列异步(同步也一样),线程:<NSThread: 0x7fe571428b10>{number = , name = main}
-- ::52.874 GCDDemo[:] dispatch_after:延时执行任务,这里2.0秒后做一些事情

多线程开发之三 GCD的更多相关文章

  1. iOS多线程开发--NSThread NSOperation GCD

    多线程 当用户播放音频.下载资源.进行图像处理时往往希望做这些事情的时候其他操作不会被中 断或者希望这些操作过程中更加顺畅.在单线程中一个线程只能做一件事情,一件事情处理不完另一件事就不能开始,这样势 ...

  2. iOS多线程开发之离不开的GCD(上篇)

    一.GCD基本概念 GCD 全称Grand Central Dispatch(大中枢队列调度),是一套低层API,提供了⼀种新的方法来进⾏并发程序编写.从基本功能上讲,GCD有点像NSOperatio ...

  3. Android多线程分析之三:Handler,Looper的实现

    Android多线程分析之三:Handler,Looper的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前文<Android多 ...

  4. iOS多线程开发资源抢夺和线程间的通讯问题

    说到多线程就不得不提多线程中的锁机制,多线程操作过程中往往多个线程是并发执行的,同一个资源可能被多个线程同时访问,造成资源抢夺,这个过程中如果没有锁机制往往会造成重大问题.举例来说,每年春节都是一票难 ...

  5. iOS多线程开发

    概览 大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的,一个复杂的多步操 ...

  6. iOS 开发线程 gcd

      基础知识: 下午9:09 一.基础概念 1.什么是GCD 全称是Grand Central Dispath 纯C语言编写,提供非常多且强大的函数,是目前推荐的多线程开发方法,NSOperation ...

  7. iOS多线程知识总结--GCD

    iOS多线程知识总结--GCD 1. iOS中苹果提供4钟方案来帮助我们实现多线程: (1) 纯C语言的pthread,偏底层,需要程序员手动管理线程的生命周期,基本不用. (2) OC语言的NSTr ...

  8. 玩转iOS开发 - 多线程开发

    前言 本文主要介绍iOS多线程开发中使用的主要技术:NSOperation, GCD. NSThread, pthread. 内容依照开发中的优先推荐使用的顺序进行介绍,涉及多线程底层知识比較多的NS ...

  9. ios多线程开发总结

    1>无论使用哪种方法进行多线程开发,每个线程启动后并不一定立即执行相应的操作,具体什么时候由系统调度(CPU空闲时就会执行). 2>更新UI应该在主线程(UI线程)中进行,并且推荐使用同步 ...

随机推荐

  1. CentOS下bond网卡聚合与网桥结合

    需求:服务器配置bond多网卡聚合,然后KVM需要使用到网桥,那么需要做到网桥绑定到bond网卡上. 实现核心: 1.bond配置参考:https://www.cnblogs.com/EasonJim ...

  2. Android中Local Service最本质的作用是什么?

    把Service等同于thread或process是一个非常常见的误解.需要强调又强调的第一点是,Android的Service是一个Context,并不必然等于一个额外的thread.如果你有仔细看 ...

  3. C#用WebBrowser与WIN API辅助模拟获取网站完整Cookie

    网上找到的可以完整获取Cookie的方法,转载一下希望能帮助更多人. 亲测可用 在Winform中使用WebBrowser控件获取网站的Cookie有时候是不完整的,默认调用Document.Cook ...

  4. WordPress主题开发:输出指定页面导航

    实例: <ul> <li class="widget widget_nav_menu"> <?php if(is_page(array(12,14,1 ...

  5. Go语言之高级篇beego框架之model设计构造查询

    一.model设计构造查询 QueryBuilder 提供了一个简便,流畅的 SQL 查询构造器.在不影响代码可读性的前提下用来快速的建立 SQL 语句. QueryBuilder 在功能上与 ORM ...

  6. SDUT 3002-素数间隙(素数筛+暴力)

    素数间隙 Time Limit: 1000ms   Memory limit: 262144K  有疑问?点这里^_^ 题目描写叙述 Neko猫是一个非常喜欢玩数字游戏的会说话的肥猫,常常会想到非常多 ...

  7. 求标准分sql

    if object_id('tempdb..#tempTable') is not null Begin drop table #tempTable End [校区],[学年],[考试年级],[考试类 ...

  8. js 引擎 和 html 渲染引擎

  9. 安装二维码、条形码识别工具zbar

    参考:http://blog.csdn.net/gaofuqi/article/details/26698547 http://www.imagemagick.org/download/ImageMa ...

  10. java中的synchronized同步代码块和同步方法的区别

    下面这两段代码有什么区别? //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized ...