文章目录
  1. 1. IOS开发中多线程
  2. 2. pThread 的使用
  3. 3. NSThread 使用
  4. 4. 创建子线程的其他方法
  5. 5. 线程的睡眠(阻塞)和退出
  6. 6. 线程同步 - 互斥锁
  7. 7. GCD
  8. 8. 截止执行
  9. 9. 延时执行
  10. 10. 一次性代码
  11. 11. 快速迭代
  12. 12. 队列组
  13. 13. NSOperation
    1. 13.1. 1、NSInvocationOperation
    2. 13.2. 2、NSBlockOperation
    3. 13.3. 3、NSOperationQueue
    4. 13.4. 4、最大并发数
    5. 13.5. 5、任务依赖 addDependency
    6. 13.6. 6、线程通信

IOS开发中多线程

  • 主线程
    • 一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”
    • 作用
      • 显示和刷新界面
      • 处理UI事件(点击、滚动、拖拽等)
    • 注意事项
      • 耗时操作不能放在主线程中没,比如资源记载,文件下载,等比较耗时间的任务,不然会卡死界面
      • 可以将耗时操作放到子线程中,将操作结果返回给主线程
  • IOS中得几种多线程实现方案

    • pThread

      • 一套通用的多线程API
      • 适用于Unix\Linux\Windows等系统
      • 跨平台\可移植
      • 使用难度大
      • C语言,手动管理线程生命周期
    • NSThread

      • 使用更加面向对象
        简单易用,可直接操作线程对象
      • OC,手动管理线程生命周期
    • GCD
      • 旨在替代NSThread等线程技术,充分利用设备的多核
      • C,自动管理
    • NSOperation
      • 基于GCD(底层是GCD)
      • 比GCD多了一些更简单实用的功能
      • 使用更加面向对象
      • OC,自动管理
    • 其中GCD和NSOperation比较常用

pThread 的使用

   // 多线程 pThread
pthread_t thread;
// 开启线程
pthread_create(&thread, NULL, run, NULL);
// 多线程 pThread
pthread_t thread1;
// 开启线程
pthread_create(&thread1, NULL, run, NULL);

void* run(void *para)
{
for (int i = 0 ;i < 10000; i ++) {

NSLog(@"run-%d----%@",i,[NSThread currentThread]);
}
return NULL;
}

NSThread 使用

  • 这种方式创建的线程,在执行完线程函数里的方法后就由系统销毁了
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// NSThread
// 创建NSThread
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun:) object:@"鸟"];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun:) object:@"蛋"];
NSThread *thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun:) object:@"碎了"];

thread1.name = @"张三";
thread2.name = @"李四";
thread3.name = @"鸟";
// 开启线程
[thread1 start];
[thread2 start];
[thread3 start];

// 这种方法创建的线程无法获得线程对象,由系统管理
[NSThread detachNewThreadSelector:@selector(thread1:) toTarget:self withObject:@"狗蛋"];

}
- (void)threadRun:(id)obj
{
for (int i = 0 ;i < 100; i ++) {

NSLog(@"run-%d-%@---%@",i,obj,[NSThread currentThread]);
}
}

创建子线程的其他方法

  • performSelectorInBackground
  • performSelector
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 创建后台线程
[self performSelectorInBackground:@selector(thread1:) withObject:@"控制器"];
// 创建任务并添加到目标线程,可以是任意线程,子线程或者主线程
[self performSelector:@selector(thread2:) onThread:[NSThread mainThread] withObject:@"Main" waitUntilDone:NO];

}

- (void)thread1:(NSString *)str
{
for (int i = 0 ;i < 100; i ++) {

NSLog(@"run-%@---%@",str,[NSThread currentThread]);
}
}

- (void)thread2:(NSString *)str
{
for (int i = 0 ;i < 100; i ++) {

NSLog(@"run-%@---%@",str,[NSThread currentThread]);
}
}

线程的睡眠(阻塞)和退出

- (void)thread1:(NSString *)str
{
for (int i = 0 ;i < 100; i ++) {

NSLog(@"run-%d-%@---%@",i,str,[NSThread currentThread]);
if (i == 19) {
sleep(2); // 当前线程睡眠2s
}
else if( i == 33 )
{
[NSThread sleepForTimeInterval:2]; // 睡眠2s
}
else if(i == 40)
{
// 获取当前时间延后2s后的时间
NSDate *endDate = [NSDate dateWithTimeIntervalSinceNow:2];

[NSThread sleepUntilDate:endDate]; // 根据日期睡眠线程
}
else if(i == 55)
{
NSLog(@"结束线程");
[NSThread exit]; // 结束线程
}
}
}

线程同步 - 互斥锁

  • 开启三个线程对同一个数据进行读写,就会出现问题,必须对读写数据进行处理,例如加锁
  • 指令@synchronized()通过对一段代码的使用进行加锁。其他试图执行该段代码的线程都会被阻塞,直到加锁线程退出执行该段被保护的代码段,也就是说@synchronized()代码块中的最后一条语句已经被执行完毕的时候。
  • @synchronized() 参数传入一个OC对象即可,self也可以
// 方法中对资源数的访问要加锁
- (void)saleTickets:(NSString *)str
{
// 三个消费者线程
while(1)
{
// 设置互斥锁
@synchronized (self)
{
// 获取资源数
NSInteger count = self.resourceCount;
if (count > 0) {
// 资源数减1
count -- ;
// 写入标记数据
self.resourceCount = count;
NSLog(@"%@消费了一个单位,还剩下%zd",[NSThread currentThread].name,count);
}
else
{
NSLog(@"资源全部使用完了");
break;
}
}
}

}

GCD

  • GCD - Grand Central Dispatch
  • GCD中任务和队列
    • 任务:执行什么操作
    • 队列:存放任务
  • 使用方式

    • 定制自己的任务,添加到队列即可。线程会有GCD自动创建和销毁。
  • 并发队列+异步任务:创建多个线程,并发执行

 /*
* 并发队列+异步任务:创建多个线程,并发执行
*/
- (void)concurrentAndAsync
{
// 创建一个并发队列
// 参数1:标识,一般用公司域名abc.com
// 参数2:队列类型:串行和并行两种
dispatch_queue_t queue = dispatch_queue_create("song.com", DISPATCH_QUEUE_CONCURRENT);
// 异步方式创建一个任务,任务不会立即执行
dispatch_async(queue, ^{
NSLog(@"1--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"5--%@--",[NSThread currentThread]);
});
// 先将以上任务全部添加完毕,然后这个函数结束,再开始并发执行线程
NSLog(@"dispatch_async--end -- %@--",[NSThread currentThread]);
// 执行结果
// dispatch_async--end -- <NSThread: 0x7ff3b1400b80>{number = 1, name = main}--
// 2--<NSThread: 0x7ff3b140bc40>{number = 2, name = (null)}--
// 1--<NSThread: 0x7ff3b16672e0>{number = 5, name = (null)}--
// 3--<NSThread: 0x7ff3b1556b20>{number = 3, name = (null)}--
// 4--<NSThread: 0x7ff3b165c760>{number = 4, name = (null)}--
// 5--<NSThread: 0x7ff3b1413aa0>{number = 6, name = (null)}--

}
  • 并发队列+同步任务:不会开启新线程,在主线程中同步执行各个子线程,也就是逐一执行,并且是添加过任务后立即执行,之后才能添加下一个任务
 /*
* 并发队列+同步任务:不会开启新线程,在父线程中同步执行各个子线程,也就是逐一执行,并且是添加过任务后立即执行,之后才能添加下一个任务
*/
- (void)concurrentAndSync
{
// 创建一个并发队列
// 参数1:标识,一般用公司域名abc.com
// 参数2:队列类型:串行和并行两种
dispatch_queue_t queue = dispatch_queue_create("song.com", DISPATCH_QUEUE_CONCURRENT);
// 同步方式创建任务,任务会立即执行,执行完成后才会继续向下执行
dispatch_sync(queue, ^{
NSLog(@"1--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"4--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"5--%@--",[NSThread currentThread]);
});
// 先将以上任务逐个依次执行完毕后才会执行这句话,并且多有任务的执行都在主线程里完成
NSLog(@"dispatch_sync--end -- %@--",[NSThread currentThread]);
// 输出结果如下
// 1--<NSThread: 0x7f9900711c90>{number = 1, name = main}--
// 2--<NSThread: 0x7f9900711c90>{number = 1, name = main}--
// 3--<NSThread: 0x7f9900711c90>{number = 1, name = main}--
// ......
// dispatch_sync--end -- <NSThread: 0x7f9900711c90>{number = 1, name = main}--
}
  • 串行队列+同步任务:不会开启新线程,在父线程中同步执行各个子线程,也就是逐一执行,并且是添加过任务后立即执行,之后才能添加下一个任务
/*
* 串行队列+同步任务:不会开启新线程,在父线程中同步执行各个子线程,也就是逐一执行,并且是添加过任务后立即执行,之后才能添加下一个任务
*/
- (void)serialAndSync
{
// 创建一个串行队列
// 参数1:标识,一般用公司域名abc.com
// 参数2:队列类型:串行和并行两种
dispatch_queue_t queue = dispatch_queue_create("song.com", DISPATCH_QUEUE_SERIAL);
// 同步方式创建任务,任务会立即执行,执行完成后才会继续向下执行
dispatch_sync(queue, ^{
NSLog(@"1--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"4--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"5--%@--",[NSThread currentThread]);
});
// 先将以上任务逐个依次执行完毕后才会执行这句话,并且多有任务的执行都在主线程里完成
NSLog(@"dispatch_sync--end -- %@--",[NSThread currentThread]);
// 1--<NSThread: 0x7fdc38f188d0>{number = 1, name = main}--
// 2--<NSThread: 0x7fdc38f188d0>{number = 1, name = main}--
// 3--<NSThread: 0x7fdc38f188d0>{number = 1, name = main}--
// 4--<NSThread: 0x7fdc38f188d0>{number = 1, name = main}--
// 5--<NSThread: 0x7fdc38f188d0>{number = 1, name = main}--
// dispatch_sync--end -- <NSThread: 0x7fdc38f188d0>{number = 1, name = main}--
}
  • 串行队列+异步任务:创建新线程,但是只会创建一个新线程,所有的任务都是在这个子线程里执行,执行顺序按照添加任务 的先后顺序,并且不是立即执行,而是等整个方法
/*
* 串行队列+异步任务:创建新线程,但是只会创建一个新线程,所有的任务都是在这个子线程里执行,执行顺序按照添加任务 的先后顺序,并且不是立即执行,而是等整个方法
*/
- (void)serialAndAsync
{
// 创建一个串行队列
// 参数1:标识,一般用公司域名abc.com
// 参数2:队列类型:串行和并行两种
dispatch_queue_t queue = dispatch_queue_create("song.com", DISPATCH_QUEUE_SERIAL);
// 同步方式创建任务,任务会立即执行,执行完成后才会继续向下执行
dispatch_async(queue, ^{
NSLog(@"1--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"5--%@--",[NSThread currentThread]);
});
// 先将以上任务逐个依次执行完毕后才会执行这句话,并且多有任务的执行都在主线程里完成
NSLog(@"dispatch_async--end -- %@--",[NSThread currentThread]);

// dispatch_async--end -- <NSThread: 0x7f97f2611bf0>{number = 1, name = main}--
// 1--<NSThread: 0x7f97f24461b0>{number = 2, name = (null)}--
// 2--<NSThread: 0x7f97f24461b0>{number = 2, name = (null)}--
}
  • 主队列 是GCD自带的一种特殊的串行队列
    • dispatch_get_main_queue()
  • 全局并发队列
    • dispatch_get_global_queue(优先级,0)
      全局并发队列的特性和手动创建的队列一样
  • 主队列+异步任务:不会创建新线程,所有的任务都是在这个父线程里执行,执行顺序按照添加任务 的先后顺序,并且不是立即执行,而是等整个方法结束后依次执行
/*
* 主队列+异步任务:不会创建新线程,所有的任务都是在这个父线程里执行,执行顺序按照添加任务 的先后顺序,并且不是立即执行,而是等整个方法结束后依次执行 */
- (void)mainAndAsync
{
NSLog(@"dispatch_async--begin -- %@--",[NSThread currentThread]);
// 创建一个串行队列
// 参数1:标识,一般用公司域名abc.com
// 参数2:队列类型:串行和并行两种
dispatch_queue_t queue = dispatch_get_main_queue();;
// 异步方式创建任务,任务不会立即执行
dispatch_async(queue, ^{
NSLog(@"1--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4--%@--",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"5--%@--",[NSThread currentThread]);
});
// 先将以上任务逐个依次执行完毕后才会执行这句话,并且多有任务的执行都在主线程里完成
NSLog(@"dispatch_get_main_queue--end -- %@--",[NSThread currentThread]);
}
  • 主队列+同步任务:这个会产生问题,死锁,添加任务到主队列的任务要求立即执行,但是主队列是串行队列,当前任务要求执行完当前任务在执行新添加的任务。结果就是:两个任务互相等待,产生死锁
/*
* 主队列+同步任务:这个会产生问题,死锁,添加任务到主队列的任务要求立即执行,但是主队列是串行队列,当前任务要求执行完当前任务在执行新添加的任务。结果就是:两个任务互相等待,产生死锁
*/
- (void)mainAndSync
{
NSLog(@"dispatch_sync--begin -- %@--",[NSThread currentThread]);

// 创建一个串行队列
// 参数1:标识,一般用公司域名abc.com
// 参数2:队列类型:串行和并行两种
dispatch_queue_t queue = dispatch_get_main_queue();
// 同步方式创建任务,任务会立即执行,执行完成后才会继续向下执行
dispatch_sync(queue, ^{
NSLog(@"1--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"4--%@--",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"5--%@--",[NSThread currentThread]);
});
// 先将以上任务逐个依次执行完毕后才会执行这句话,并且多有任务的执行都在主线程里完成
NSLog(@"dispatch_get_main_queue--end -- %@--",[NSThread currentThread]);
}

截止执行

  • dispatch_barrier_async :截断线程执行,以上两个任务完成后才会执行这个任务,并在完成barrier后继续执行下面的任务
  • 任务1,2的执行始终在任务3,4之前
  • 只有在并发队列中才有阶段功能

延时执行

- (void)delay
{
// 延时执行
NSLog(@"start--time:%@",[NSDate date]);
// NSObject 方法
[self performSelector:@selector(run) withObject:nil afterDelay:2];
// NSTimer
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:NO];
// GCD
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"run--time:%@",[NSDate date]);
});
}

一次性代码

  • 一次性代码内部默认就是线程安全的,不会出现多个线程同时访问内部代码
- (void)excuteOnce
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只执行一次---%@",[NSThread currentThread]);
});
NSLog(@"excuteOnce--%@",[NSThread currentThread]);
}

快速迭代

// 快速迭代,顺序不确定
- (void)apply
{
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"dispatch_apply--%ld--%@",index,[NSThread currentThread]);
});
}

队列组

/*
* 队列组:先执行async里的任务,最后执行notify任务
*/
- (void)groupAndAsync
{
dispatch_group_t group = dispatch_group_create();

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 先执行3个耗时操作
dispatch_group_async(group, queue, ^{
for (int i = 0 ; i < 1000; i ++) {
NSLog(@"1--%@--",[NSThread currentThread]);
}
});

dispatch_group_async(group, queue, ^{
for (int i = 0 ; i < 1000; i ++) {
NSLog(@"2--%@--",[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0 ; i < 1000; i ++) {
NSLog(@"3--%@--",[NSThread currentThread]);
}
});
// 等到以上任务完成后才会执行这个notify任务
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"main--%@--",[NSThread currentThread]);
});
}

NSOperation

  • NSOperation是一抽象类,只能使用它的子类NSBlockOperation和NSInvocationOperation,或者自定义NSOperation。

1、NSInvocationOperation

  • 使用方法initWithTarget:进行初始化。
  • 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作。
  • 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作。
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start];

2、NSBlockOperation

  • 使用blockOperationWithBlock添加任务,或者addExecutionBlock添加多个任务。
  • 只要添加的任务数量大于1就会自动创建多个线程,异步执行。
NSBlockOperation *ope1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"0---%@",[NSThread currentThread]);
}];

[ope1 addExecutionBlock:^{
NSLog(@"1---%@",[NSThread currentThread]);

}];
[ope1 addExecutionBlock:^{
NSLog(@"2---%@",[NSThread currentThread]);

}];
[ope1 addExecutionBlock:^{
NSLog(@"3---%@",[NSThread currentThread]);

}];
[ope1 start];

3、NSOperationQueue

  • NSOperation可以调用start方法来执行任务,但默认是同步执行的。
  • 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作。
// 添加到队列的任务会自动并发执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *ope1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1---%@",[NSThread currentThread]);
}];
NSBlockOperation *ope2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2---%@",[NSThread currentThread]);
}];

NSBlockOperation *ope3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3---%@",[NSThread currentThread]);
}];

// 添加到队列会自动执行,不需要手动调用start方法
[queue addOperation:ope1];
[queue addOperation:ope2];
[queue addOperation:ope3];
[queue addOperationWithBlock:^{
NSLog(@"4---%@",[NSThread currentThread]);
}];

4、最大并发数

  • 设置队列的最大并发数
  • maxConcurrentOperationCount = 2;,同一时刻只会有两条线程在执行
  • [queue setMaxConcurrentOperationCount:2]; // 这样设置
  • 如果设置为1,就变成串行队列了
// 最大并发数,同一时刻只会有两条线程在执行
queue.maxConcurrentOperationCount = 2;

5、任务依赖 addDependency

  • 多个线程之间可以建立依赖关系
// 添加依赖:任务1和2的执行在3之前
[ope3 addDependency:ope1];
[ope3 addDependency:ope2];

6、线程通信

  • 获取主队列mainQueue,将任务添加到主队列
  • 如果定义的时全局的队列,可以在其他地方向这个队列添加任务。
// 获取主队列
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"main---%@",[NSThread currentThread]);
}];
文章目录
  1. 1. IOS开发中多线程
  2. 2. pThread 的使用
  3. 3. NSThread 使用
  4. 4. 创建子线程的其他方法
  5. 5. 线程的睡眠(阻塞)和退出
  6. 6. 线程同步 - 互斥锁
  7. 7. GCD
  8. 8. 截止执行
  9. 9. 延时执行
  10. 10. 一次性代码
  11. 11. 快速迭代
  12. 12. 队列组
  13. 13. NSOperation
    1. 13.1. 1、NSInvocationOperation
    2. 13.2. 2、NSBlockOperation
    3. 13.3. 3、NSOperationQueue
    4. 13.4. 4、最大并发数
    5. 13.5. 5、任务依赖 addDependency
    6. 13.6. 6、线程通信