进程和线程是操作系统中的概念,以我的理解呢 一个CPU就好比是一个工厂,而一个工厂里面可以有多个车间,一个进程就好比是一个工厂中的一个车间,而线程就好比是车间里的工人,也就是说一个进程中可以有多个线程。
那么在iOS开发过程中,怎么样开始一个线程呢,有以下三种方法:
1.performSelectorInBackground
例如:开启一个线程的方法
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
2.使用NSThread 静态方法 (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
也就是直接创建了一个线程,SEL参数指是这个线程需要执行的作务, 使用这个方法的话,线程直接启动
例如:[NSThread detachNewThreadSelector:@selector(doThing) toTarget:self withObject:nil];
3.创建NSThread 的对象, 然后调用start来启动线程
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
例如:NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(doThing) object:nil];
thread.name = @"QingYun";
//启动线程
[thread start];
知道了怎么创建一个线程,那么怎么样创建一个多线程呢?
有两种方法可以实现多线程
1. 先创建NSOperation 子类对象, 然后使用NSOperationQueue来实现多线程
使用- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
例如:
//创建一个多线程的队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //创建一个操作对象,这个操作对象定义了多线程需要执行作务 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doThing) object:nil]; //将操作对象放到这个队列里,这样线程就可以直接启动 [queue addOperation:operation];
2.创建NSBlockOperation对象然后使用NSOperationQueue来实现多线程
NSOperationQueue *queue = [[NSOperationQueuealloc] init];
NSBlockOperation *operation = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@"do thing....");
//当前线程休眠10秒
[NSThreadsleepForTimeInterval:10];
NSThread *thread = [NSThread currentThread];//查看当前的进程
if ([thread isMainThread]) {//判断当前进程是不是主线程
NSLog(@"main thread");
}else
{
NSLog(@"peer thread");
}
NSLog(@"DONE.%@",thread.name);
}];
[queue addOperation:operation];
单线程和多线程的使用大家都知道了,我们接下来说一个更好用技术,GCD ,什么是GCD呢?
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。该方法在Mac OS X 10.6雪豹中首次推出,并随后被引入到了iOS4.0中。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术,它看起来象就其它语言的闭包(Closure)一样,但苹果把它叫做blocks。
让我们来看一个编程场景。在iphone上做一个下载网页的功能,该功能非常简单,就是在iphone上放置一个按钮,点击该按钮时,显示一个转动的圆圈,表示正在进行下载,下载完成之后,将内容加载到界面上的一个文本控件中。如果没有提示会给用户带来不好的用户体验。
GCD就是用来解决这个问题的,dispatch是使用C风格的代码写的。
//创建一个队列
dispatch_queue_t queque = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async (queque,^{ NSLog(@"do thing...."); //当前线程休眠10秒 [NSThread sleepForTimeInterval:5]; NSThread *thread = [NSThread currentThread]; if ([thread isMainThread]) { NSLog(@"main thread"); }else { NSLog(@"peer1 thread"); } NSLog(@"DONE.%@",thread.name); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ NSLog(@"do another thing...."); //当前线程休眠10秒 [NSThread sleepForTimeInterval:5]; NSThread *thread = [NSThread currentThread]; if ([thread isMainThread]) { NSLog(@"main thread"); }else { NSLog(@"peer2 thread"); } NSLog(@"DONE.%@",thread.name); });
其中,dispatch_get_global_queue(dispatch_queue_priority_t priority,unsignedlong flags);中第一个参数的优先级有如下几种:
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_async(dispatch_queue_t queue, dispatch_block_t block),这个方法是异部的,即相当于使用的多线程,多线程的优先级是在程序最开始优先级级别高的会先调用,因为这两个线程都是子线程,有使用的过程中会存在竟争状态。 ,还有一个方法是dispatch_sync(dispatch_queue_t queue, dispatch_block_t block),这个方法模拟的是单线程。
程序运行的结果是:
2014-05-15 16:29:54.070 ThreadSample[4465:60b] BUTTON ACTION
2014-05-15 16:29:54.070 ThreadSample[4465:1303] do thing....
2014-05-15 16:29:54.070 ThreadSample[4465:3903] do another thing....
2014-05-15 16:29:59.073 ThreadSample[4465:1303] peer1 thread
2014-05-15 16:29:59.074 ThreadSample[4465:1303] DONE.
2014-05-15 16:29:59.073 ThreadSample[4465:3903] peer2 thread
2014-05-15 16:29:59.075 ThreadSample[4465:3903] DONE.
下面来模拟一个从服务器上获取数据的例子:
#import "QYViewController.h"
@interface QYViewController ()
//在函数中定义的全局变量 @property (weak, nonatomic) IBOutlet UIButton *buttonStart; @property (weak, nonatomic) IBOutlet UITextView *resultTextView; @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator; @end
@implementation QYViewController
- (void)viewDidLoad
{
[superviewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[superdidReceiveMemoryWarning];
// Dispose of any resources that can be recreated
}
@end
然后在@implementation 和@end之间添加模拟的方法:如下所示:
//模拟从服务器上获取数据 - (NSString*)fetchSomeThingFromServer { [NSThread sleepForTimeInterval:1]; return @"Hi there"; } //模拟处理从服务器上取下的数据的过程 - (NSString*)processData:(NSString*)data { [NSThread sleepForTimeInterval:2]; // 主功能是:如果取下的数据是大不写, 全部转换成大写 return [data uppercaseString]; } //模拟第一次处理从服务器获取的数据 - (NSString*)calculateFirstResult:(NSString*)data { [NSThread sleepForTimeInterval:3]; return [NSString stringWithFormat:@"Number of chars: %d",data.length]; } //模拟第二次处理从服务器取下的值 - (NSString*)calculateSecondResult:(NSString*)data { [NSThread sleepForTimeInterval:4]; // 将字符串data中的所有大写的E字母转换成小写 return [data stringByReplacingOccurrencesOfString:@"E" withString:@"e"]; }
添加- (IBAction)doSomeWork:(id)sender {
// 计算一下从开始工作, 到结束一共花费了多少CPU的时间 NSDate *startTime = [NSDate date];
//当获取数据的时候将按钮设置为不可点击 self.buttonStart.enabled = NO; self.buttonStart.alpha = 0.5;
//添加一个风火轮,当获取数据的时候开始转动 [self.activityIndicator startAnimating]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//以下两个程序的执行顺序不能改变,所以不能使用分组 NSString *fetchData = [self fetchSomeThingFromServer]; NSString *processData = [self processData:fetchData]; // NSString *firstResult = [self calculateFirstResult:processData]; // NSString *secondResult = [self calculateSecondResult:processData]; // 关于block块的变量作用范围
// block块中可以使用block块外的变量,但是它使用的时候是把block外的变量拷贝过来当做常量使用,若不想当做就是使用应该在变量定义的时候前面加上__block
__block NSString *firstResult; __block NSString *secondResult; // 创建一个group dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ firstResult = [self calculateFirstResult:processData]; NSLog(@"%@",firstResult); }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ secondResult = [self calculateSecondResult:processData]; NSLog(@"%@",secondResult); }); // 根据计算后的结果, 拼接成新的包含换行的字符串 dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *resultSummary = [NSString stringWithFormat:@"First:[%@] Second:[%@]",firstResult,secondResult]; // 将计算好的结果的字符串放在我们的界面上。
//这是需要注意的是所有的关于视图的操作均会在主线程上操作
dispatch_sync(dispatch_get_main_queue(), ^{ self.resultTextView.text = resultSummary; self.buttonStart.enabled = YES; self.buttonStart.alpha = 1.0f; [self.activityIndicator stopAnimating]; });
//结束时间必须放在第一个异步的数据块里面,因为使用的异步操作,就相当于是多线程,如果放在外面会直接走下一步,计算不出准确的时间 NSDate *endTime = [NSDate date]; NSLog(@"Completed in %f seconds",[endTime timeIntervalSinceDate:startTime]); }); }); }
模拟的结果如下: