N
Published on
· Last modified on
· Public

iOS 多线程的使用笔记

进程的特征

1.  一个用户进程不可以直接访问其他进程的地址空间
2.  多个线程之间不会互相影响
3.  独立性、动态性、并发性

· CPU在某个时间点上其实只能执行一个程序,CPU不断在这些进程之间轮换执行,这速度是很快的。
· 一个程序运行后至少有一个进程,一个进程包含多个线程。
· 操作系统可以有多个进程,一个进程可以有多个线程。
· 进程间不能共享内存,但线程之间共享内存非常容易。

一、创建NSThread有两种方式

- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)arg:
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)arg:

NSThread的一些方法

+ (NSThread *)currentThread // 返回当前线程
- (void)start // 调用该方法线程不会立即执行,可以用[NSThread sleepForTimeInterval:0.001]让当前主线程睡眠一会,让子线程得到立刻执行得机会。

NSThread的cancel方法只是改变isCancelled的值为YES,不是真正终止该线程。

if([NSThread currentThread].isCancelled)
{
    [NSThread exit]; // 终止当前线程
}

需要发送[thread cancel]消息。

线程睡眠

+ (void)sleepUntilDate:(NSDate *)aDate // 让当前线程暂停到aDate代表的时间
+ (void)sleepForTimeInterval:(NSTimeInterval)timeInterval // 让当前线程暂停N秒

iOS规定只能在UI线程中修改UI控件的属性

// 回到主线程来执行修改UI的操作
// 这个是NSObject的分类方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

改变线程的优先级

· 优先级越高,获得的执行机会越大
· 每个子线程默认优先级为0.5
//  返回当前线程的优先级
+ (double)threadPriority
- (double)threadPriority
// 设置当前线程的优先级
+ (void)setThreadPriority:(double)p
- (void)setThreadPriority:(double)p // 范围0~1

使用@synchronized{…}实现同步

当两个线程同时对一个共享资源进行并发访问时,可能会引起问题。(调用[NSThread sleepForTimeInterval:0.001]来显式引起问题) 这是一种“加锁-》修改-》释放锁”的模式

@synchronized(self)
{
    // …
} // 同步代码快结束,该线程释放同步锁

使用同步锁(NSLock)

NSLock和@synchronized语句相似,只是显式地调用lock、unlock等方法

@implementation x
NSLock *lock;
- (id)init
{
    self = [super init];
    if (self)
    {
        lock = [[NSLock alloc] init];
    }
    return self;
}

- (void)m
{
    [lock lock];
    // 需要保证线程安全的代码
    // …
    [lock unlock];
}

二、使用GCD实现多线程

使用GCD主要包含两个步骤:

1.队列:并行队列(同时处理多个任务)和串行队列(同一时间只处理一个任务),dispatchqueuet代表一个队列。 2.任务:在队列里面执行任务

创建和访问队列的方法:

dispatch_queue_t dispatch_get_current_queue(void) // 获取当前执行代码所在的队列
dispatch_queue_t dispatch_get_global_queue(long priority, unsigned long flags)

获取系统全局并发队列,第一个参数可以是DISPATCH_QUEUE_PRIORITY_HEIGHT/DISPATCH_QUEUE_PRIORITY_DEFAULT/DISPATCH_QUEUE_PRIORITY_LOW/DISPATCH_QUEUE_PRIORITY_BACKGROUND。第二个参数没用,暂时写0

dispatch_queue_t dispatch_get_main_queue(void) // 获取主线程串行队列
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) // 通过字符串标识创建队列。

第二个参数可选值有DISPATCH_QUEUE_SERIAL(串行队列)/DISPATCH_QUEUE_CONCURRENT(并行队列)。 此函数在没用ARC情况下需要用dispatch_release()函数释放

const char *dispatch_queue_get_label(dispatch_queue_t queue) //获取指定队列得字符串标识

异步提交任务

void dispatch_async(dispatch_queue_t queue, dispatch_block_t block) // 以异步的方式提交给指定队列
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block) // 以同步的方式提交给指定队列,执行完一个再一个,线程阻塞
void dispatch_after(dispatch_time_t when, dispatch_queue_t_ queue, dispatch_block_t block) // 在when指定时间点执行异步
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)) // 异步的方式提交,多次重复执行代码块
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block) // 只执行一次代码块
// 创建串行队列
serialQueue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL)
// 创建并发队列
concurrentQueue =  dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT)

// 把代码块提交给串行队列,一个一个代码块执行
dispatch_async(serialQueue, ^(void){
    // …
}); 

// 把代码块提交给并行队列,多个代码块同时执行
dispatch_async(concurrentQueue, ^(void){
    // …
});

使用GCD的简单例子

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HEIGHT, 0), ^(void) {
    // …
    dispatch_async(dispatch_get_main_queue(), ^{
        // …
    });
    // …
});

多次执行的任务

dispatch_apply(5, disptach_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t time){
    // time表示当前正在执行第几次
    NSLog(@"=== 执行%lu次===", time);
});
// 启动了5个线程重复执行该代码块

只执行一次的任务

static dispatch_once_t onceToken; // 这个标识用来判断是否已经执行
dispatch_once(&onceToken, ^(void){
    // …
});
// 它是直接在主线程执行该函数提交的代码块,只执行一次

三、使用NSOperation与NSOperationQueue

NSOperationQueue 一个FIFO队列

NSOperation 一个任务

使用方法: 1.创建NSOperationQueue 2.把NSOperation提交给NSOperationQueue

NSOperationQueue的常用方法:

+ (id)currentQueue // 返回当前NSOperation的NSOperationQueue队列

+ (id)mainQueue // 返回主线程的NSOperationQueue队列

- (void)addOperation:(NSOperation *)operation // 将NSOperation添加到队列中

- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait // 将ops包含的operation的提交到队列,wait为YES会阻塞线程,直到ops包含的所有operation全部提交完成。

- (NSArray *)operations // 只读,返回该queue包含的所有operation

- (NSUInteger)operationCount // 只读,返回该queue包含的所有opertaion数量

- (void)cancelAllOpertaions // 取消所有队列中正在排队和执行的NSOperation

- (void)waitUntilAllOperationsAreFinished // 先阻塞线程,直到所有operation执行完成才解除是阻塞线程。

- (void)setMaxConcurrentOperationCount:(NSInteger)cnt // 设置queue最大支持的并发线程数量

- (void)addOperationWithBlock:(void (^)(void))block // 把block放到queue执行

NSInvocationOperationNSBlockOperation都是继承于NSOperation 它们的初始化方法:

NSInvocationOpertaion *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImageFromURL:) object:url];

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(void){
    // …
}];

使用NSBlockOperation下载图片

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 10;
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(void){
    // …
    // 回到主线程更新UI
    [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES]
    // …
}];
[queue addOperation:operation];

定义NSOperation的子类

继承NSOperation然后重写- (void)main,该方法将作为线程的执行体

- (id)initWithURL:(NSURL *)url imageView :(UIImageView *)iv
{
    if (self = [super init])
    {
        _imageView = iv;
        _url = url;
    }
    return self;
}

- (void)main
{
    NSData *data = [[NSData alloc] initWithContentsOfURL:self.url];
    UIImage *image = [[UIImage alloc] initWithData:data];
    if(image != nil)
    {
        [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
    }
}

- (void)updateUI:(UIImage *)image
{
    self.imageView.image = image;
}

然后只需要将该类实例添加到NSOperationQueue即可

L
Published on

大神,厉害!!!

N
Published on
liuruixuan:

大神,厉害!!!

小菜一碟! :-)

I
Published on

求星。

N
Published on
ivosun:

求星。

已星!

H
Published on

已赞!

P
Published on
Sign in or Sign up Leave Comment