星空网 > 软件开发 > 操作系统

4.1/4.2 多线程进阶篇上(Pthread NSThread)

因为Pthread很少用到,所以对于Pthread的知识没有抠那么细致,所以将Pthread和 NSThread放在了一起。

 

4.1 Pthread


4.1-1.0 创建线程 - pthread_create

 1 /* 2 <#pthread_t *restrict#> 线程的id,指向线程标识符的指针,C 语言中类型的结尾通常 _t/Ref,而且不需要使用 * 3 <#const pthread_attr_t *restrict#> 用来设置线程的属性 (一般为 NULL) 4 <#void *(*)(void *)#> 新建立的线程执行代码的函数,线程开启后需要调用的函数或方法 (指向函数的指针) 5 <#void *restrict#>   运行函数的参数,线程的限制 (一般为 NULL) 6 */ 7  8 返回值: 9 - 若线程创建成功,则返回010 - 若线程创建失败,则返回出错编号11 12 pthread_create(13      <#pthread_t *restrict#>,  // pthread_t :线程标识符.14      <#const pthread_attr_t *restrict#>,15      <#void *(*)(void *)#>,16      <#void *restrict#>17 );

 

4.1-1.1 __bridge 桥接

__bridge 桥接:
1、在c语言和OC之间,对数据类型进行转成换
2、通过桥接告诉c语言的函数,name就由C语言去管了
桥接的目的 : 
就是为了告诉编译器如何管理内存,为OC添加自动内存管理操作
 
小结 :

  • 在 C 语言中,没有对象的概念,对象是以结构体的方式来实现的
  • 通常,在 C 语言框架中,对象类型以 _t/Ref 结尾,而且声明时不需要使用 *
  • C 语言中的 void * 和 OC 中的 id 是等价的
  • 在混合开发时,如果在 C 和 OC 之间传递数据,需要使用 __bridge 进行桥接,
  • 桥接的添加可以借助 Xcode 的辅助功能添加
 
number = 1: 表示 主线程
number != 1: 表示 子线程
 
C语言中 void * == OC中的id
C语言的数据类型,一般以  Ref / _t
  • OC框架 Foundation
  •   C语言 Core Foundation
 
 4.1-1.2【代码】Pthread
 1 首先导入头文件 2  3 #import <pthread.h> 4  5 代码创建: 6  7 // 创建线程,并且在线程中执行 demo 函数 8 - (void)pthreadDemo { 9 10  pthread_t threadId = NULL;11  NSString *str = @"Hello Pthread";12 13  int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(str));14 15  if (result == 0) {16   NSLog(@"创建线程 OK");17  } else {18   NSLog(@"创建线程失败 %d", result);19  }20 }21 22 // 后台线程调用函数23 void *demo(void *params) {24  NSString *str = (__bridge NSString *)(params);25 26  NSLog(@"%@ - %@", [NSThread currentThread], str);27 28  return NULL;29 }

 

 

4.2  NSThread


 

4.2-1.0 创建线程

  • 第一种:通过NSThread的对象方法 (alloc / init - start)
  • 第二种:通过NSThread的类方法    (detachNewThreadSelector)
  • 第三种:通过NSObject的方法
 4.2-1.1 创建线程1 - 对象方法alloc/init
一个NSThread对象就代表一条线程
1 创建方式1 : 通过NSThread的对象方法 (先创建初始化线程alloc/init , 再 start 开启线程)  ——调试方便2 3 NSThread *thread = [[NSThread alloc]initWithTarget:<#(nonnull id)#>4                      selector:<#(nonnull SEL)#>5                       object:<#(nullable id)#> ];


 1 #import "ViewController.h" 2  3 @interface ViewController () 4 @end 5  6 @implementation ViewController 7  8 - (void)viewDidLoad { 9  [super viewDidLoad];10  // Do any additional setup after loading the view, typically from a nib.11 }12 13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {14  [self threadDemo1];15 }16 17 #pragma mark - 对象方法alloc/init18 /*19  - 在 OC 中,任何一个方法的代码都是从上向下顺序执行的20  - 同一个方法内的代码,都是在相同线程执行的(block除外)21 */22 - (void)threadDemo1 {23  NSLog(@"before %@", [NSThread currentThread]);24 25  // 线程一启动,就会在线程thread中执行self的run方法26  NSThread *thread = [[NSThread alloc] initWithTarget:self27                        selector:@selector(longOperation:)28                        object:@"THREAD"];29 30  //开启线程,通过start方法,就会将我们创建出来的当前线程加入到`可调度线程池`,供CPU调度31  //[thread start];执行后,会在另外一个线程执行 longOperation: 方法32  [thread start];33 34  NSLog(@"after %@", [NSThread currentThread]);35 }36 37 - (void)longOperation:(id)obj {38  NSLog(@"%@ - %@", [NSThread currentThread], obj);39 }40 41 - (void)didReceiveMemoryWarning {42  [super didReceiveMemoryWarning];43  // Dispose of any resources that can be recreated.44 }45 46 @end

打印结果:2016-03-17 18:19:41.878 创建线程1 - 对象方法[2543:387435] before <NSThread: 0x7ffd28c00ca0>{number = 1, name = main}2016-03-17 18:19:41.880 创建线程1 - 对象方法[2543:387711] <NSThread: 0x7ffd28d77be0>{number = 2, name = (null)} - THREAD2016-03-17 18:19:41.880 创建线程1 - 对象方法[2543:387435] after <NSThread: 0x7ffd28c00ca0>{number = 1, name = main}

  

4.2-1.1.1 Target

NSThread 的实例化方法中的 target 指的是开启线程后,在线程中执行 哪一个对象 的 @selector 方法
1 NSThread *thread = [[NSThread alloc] initWithTarget:self.person2                       selector:@selector(longOperation:)3                       object:@"THREAD"];4 5 [thread start];

  • 通过指定不同的 target 会在后台线程执行该对象的 @selector 方法
  • 不要看见 target 就写 self

 



4.2-1.2 创建线程2 - 类方法

1 创建方式2 : 通过NSThread的类方法 (创建线程后直接自动启动线程)2 3 [NSThread detachNewThreadSelector:<#(nonnull SEL)#>4             toTarget:<#(nonnull id)#>5            withObject:<#(nullable id)#> ];

 1 #import "ViewController.h" 2  3 @interface ViewController () 4 @end 5  6 @implementation ViewController 7  8 - (void)viewDidLoad { 9  [super viewDidLoad];10  // Do any additional setup after loading the view, typically from a nib.11 }12 13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {14  [self threadDemo2];15 }16 17 #pragma mark - 类方法18 - (void)threadDemo2 {19  NSLog(@"before %@", [NSThread currentThread]);20 21  // detachNewThreadSelector 类方法不需要启动,会自动创建线程并执行@selector方法22  // 它会自动给我们做两件事 : 1.创建线程对象 2.添加到`可调度线程池`23  // 通过NSThread的类和NSObject的分类方法直接加入到可调度线程池里面去,等待CPU调度24  [NSThread detachNewThreadSelector:@selector(longOperation:)25               toTarget:self26             withObject:@"DETACH"];27 28  NSLog(@"after %@", [NSThread currentThread]);29 }30 31 - (void)longOperation:(id)obj {32  NSLog(@"%@ - %@", [NSThread currentThread], obj);33 }34 35 - (void)didReceiveMemoryWarning {36  [super didReceiveMemoryWarning];37  // Dispose of any resources that can be recreated.38 }39 40 @end

打印结果:2016-03-17 18:36:05.339 创建线程2 - 类方法[2647:404930] before <NSThread: 0x7fddf8f01eb0>{number = 1, name = main}2016-03-17 18:36:05.340 创建线程2 - 类方法[2647:404930] after <NSThread: 0x7fddf8f01eb0>{number = 1, name = main}2016-03-17 18:36:05.340 创建 线程2 - 类方法[2647:405061] <NSThread: 0x7fddf8e0e7a0>{number = 2, name = (null)} - DETACH

 

4.2-1.3 创建线程3 - 分类方法(NSObject)

1 创建方式3 : 通过NSObject的分类方法 (隐式创建并直接自动启动线程)  ——推荐,开发常用2 3 // 此方法在后台线程中执行 (即是 : 在子线程中执行)4 [self performSelectorInBackground:<#(nonnull SEL) #>5            withObject:<#(nullable id) #> per];

 1 #import "ViewController.h" 2  3 @interface ViewController () 4 @end 5  6 @implementation ViewController 7  8 - (void)viewDidLoad { 9  [super viewDidLoad];10  // Do any additional setup after loading the view, typically from a nib.11 }12 13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {14  [self threadDemo3];15 }16 17 #pragma mark - 分类方法18 - (void)threadDemo3 {19  NSLog(@"before %@", [NSThread currentThread]);20 21  // performSelectorInBackground 是NSObject的分类方法,会自动在后台线程执行@selector方法22  // 没有 thread 字眼,隐式创建并启动线程23  // 所有 NSObject 都可以使用此方法,在其他线程执行方法24  // 通过NSThread的类和NSObject的分类方法直接加入到可调度线程池里面去,等待CPU调度25  // PerformSelectorInBackground 可以让方便地在后台线程执行任意NSObject对象的方法26  [self performSelectorInBackground:@selector(longOperation:)27             withObject:@"PERFORM"];28 29  NSLog(@"after %@", [NSThread currentThread]);30 }31 32 - (void)longOperation:(id)obj {33  NSLog(@"%@ - %@", [NSThread currentThread], obj);34 }35 36 - (void)didReceiveMemoryWarning {37  [super didReceiveMemoryWarning];38  // Dispose of any resources that can be recreated.39 }40 41 @end

打印结果:2016-03-17 18:41:58.696 创建线程3 - 分类方法[2711:412522] before <NSThread: 0x7ff078c02320>{number = 1, name = main}2016-03-17 18:41:58.696 创建线程3 - 分类方法[2711:412522] after <NSThread: 0x7ff078c02320>{number = 1, name = main}2016-03-17 18:41:58.697 创建线程3 - 分类方法[2711:412751] <NSThread: 0x7ff078c0e390>{number = 2, name = (null)} - PERFORM

  

方式2和方式3的优缺点 :
优点:简单快捷
缺点:无法对线程进行更详细的设置

 

4.2-2.0 线程属性

 1 主线程相关方法 : 2  3 + (NSThread *)mainThread; // 获得主线程 4 - (BOOL)isMainThread; // 是否为主线程 5 + (BOOL)isMainThread; // 是否为主线程 6  7 NSThread *main = [NSThread mainThread];  // + (NSThread *)mainThread; 获得主线程 8  9 [NSThread isMainThread]; // + (BOOL)isMainThread;  类方法判断,该方法是否为主线程10 11 [main isMainThread];   // - (BOOL)isMainThread;  对象方法判断,该对象是否为主线程

 1 其他用法: 2 (1) currentThread - 获得当前线程 : 3  4 举例 : 5 NSThread *current = [NSThread currentThread]; //获得当前线程 6  7 (2) threadPriority - 线程的调度优先级 : 8  9 优先级,是一个浮点数,取值范围从 0~1.0 默认优先级是0.5 值越大,优先级越高10 11 优先级高只是保证 CPU 调度的可能性会高12 13 建议:在开发的时候,不要修改优先级14 多线程的目的:是将耗时的操作放在后台,不阻塞主线程和用户的交互!15 多线程开发的原则:简单16 17 //返回当前方法所在线程的优先级18 + (double)threadPriority;19 举例:[NSThread threadPriority];20 21 //设置线程的优先级22 + (BOOL)setThreadPriority:(double)p;23 举例:self.thread1.threadPriority = 1.0;24 25 - (double)threadPriority;//返回当前方法所在线程的优先级26 - (BOOL)setThreadPriority:(double)p;//设置线程的优先级27 28 (3) name - 线程的名字 : 在大的商业项目中,通常需要在程序崩溃时,获取程序准确执行所在的线程29 30 - (void)setName:(NSString *)n;  //set 方法31 - (NSString *)name;       //get 方法32 33 举例:34 thread.name = @"线程A";35 36 (4) stackSize - 栈区大小37 38 - 默认情况下,无论是主线程还是子线程,栈区大小都是 512K39 - 栈区大小虽然可以设置,但是我们一般都使用系统默认的大小就行了40 41 举例:42 [NSThread currentThread].stackSize = 1024 * 1024;

代码示例:

 1 // 线程属性 2 - (void)threadProperty { 3  NSThread *t1 = [[NSThread alloc] initWithTarget:self 4                      selector:@selector(demo) 5                      object:nil]; 6  7  // 1. 线程名称 8  t1.name = @"Thread AAA"; 9 10  // 2. 优先级11  t1.threadPriority = 0;12 13  [t1 start];14 15  NSThread *t2 = [[NSThread alloc] initWithTarget:self16                      selector:@selector(demo)17                      object:nil];18  // 1. 线程名称19  t2.name = @"Thread BBB";20  // 2. 优先级21  t2.threadPriority = 1;22 23  [t2 start];24 }25 26 - (void)demo {27  for (int i = 0; i < 10; ++i) {28   // 堆栈大小29   NSLog(@"%@ 堆栈大小:%tuK", [NSThread currentThread],[NSThread currentThread].stackSize / 1024);30  }31 32  // 模拟崩溃33  // 判断是否是主线程34    if (![NSThread currentThread].isMainThread) {35      NSMutableArray *a = [NSMutableArray array];36      [a addObject:nil];37    }38 }

 

4.2-3.0 线程状态/线程生命周期 

  • 新建状态
  • 就绪状态/启动状态 : 线程在可调度线程池中
  • 运行状态
  • 阻塞状态/暂停线程 : 线程不在可调度线程池中,但是仍然存在内存中,只是不可用
  • 死亡状态 : 线程不在内存中
 (1)新建状态 : 实例化线程对象
说明:创建线程有多种方式,这里不做过多的介绍
 
 
(2)就绪状态 / 启动线程: ( 进入就绪状态 ->运行状态。当线程任务执行完毕,自动进入死亡状态 )
线程开启 : 线程进入可调度线程池
 
  • 向线程对象发送 start 消息,线程对象被加入可调度线程池等待 CPU 调度
  • detachNewThreadSelector 方法和 performSelectorInBackground 方**直接实例化一个线程对象并加入可调度线程池

(3)运行状态:
  • CPU 负责调度可调度线程池中线程的执行
  • 线程执行完成之前(死亡之前),状态可能会在就绪和运行之间来回切换
  • 就绪和运行之间的状态变化由 CPU 负责,程序员不能干预
 
  • 当 CPU 调度当前线程 , 进入运行状态
  • 当 CPU 调度其他线程 , 进入就绪状态
(4)阻塞状态 / 暂停线程 : 当满足某个预定条件时,可以使用休眠或锁阻塞线程执行
 1 方法执行过程,符合某一条件时,可以利用 sleep 方法让线程进入 阻塞 状态 2  3 sleepForTimeInterval:  //休眠指定时长  (从现在起睡多少秒) 4 sleepUntilDate:     //休眠到指定日期 (从现在起睡到指定的日期) 5 @synchronized(self) { }  //互斥锁 6  7 (1)阻塞2秒 8 [NSThread sleepForTimeInterval:2]; // 阻塞状态 9 10 (2)以当前时间为基准阻塞4秒11 NSDate *date = [NSDate dateWithTimeIntervalSinceNow:4.0]; //从现在开始多少秒12 [NSThread sleepUntilDate:date]; //睡眠多少秒

(5)死亡状态 (一旦线程停止或死亡了,就不能再次开启任务 , 后续的所有代码都不会被执行 )
(1) 正常死亡
  • 线程执行完毕
 
(2) 非正常死亡
  • (**)          当满足某个条件后,在线程内部自己中止执行
  • (被逼着死亡) 当满足某个条件后,在主线程给其它线程打个死亡标记(下圣旨),让子线程自行了断.
 
注意:在终止线程之前,应该注意释放之前分配的对象! 

 

 生命周期示意图:
4.1/4.2 多线程进阶篇上(Pthread  NSThread)images/loading.gif' data-original="http://images2015.cnblogs.com/blog/731328/201603/731328-20160325103511308-1180733546.png" />


 

 


 代码示例:
 1 #import "ViewController.h" 2  3 @interface ViewController () 4 @end 5  6 @implementation ViewController 7  8 - (void)viewDidLoad { 9  [super viewDidLoad];10  // Do any additional setup after loading the view, typically from a nib.11 }12 13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {14 15  // 1.在主线程中创建一个子线程(实例化线程对象) ---> 新建状态16  NSThread *Th =17    [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];18 19  // 2.将 Th 线程加入到可调度线程池,等待CPU调度--->就绪状态20  [Th start];21 22  // 3.让主线程阻塞,让当前线程(主线程)休眠23  [NSThread sleepForTimeInterval:1.0];24 25  // 4.在主线程给 Th 线程打死亡标签26  [Th cancel]; //只是打了个标签,并没有执行,需要在子线程中27 }28 29 // Th 线程---> 运行状态30 - (void)run {31 32  NSThread *huThread = [NSThread currentThread];33 34  CGMutablePathRef path = CGPathCreateMutable();35 36  for (int i = 0; i < 30; i++) {37   if ([huThread isCancelled]) {38    NSLog(@"good bye1");39    return; // --->非正常死亡(被逼着死亡)40   }41 42   if (i == 5) {43    [NSThread sleepForTimeInterval:3.0]; //--->huThread阻塞状态3秒44    // [NSThread sleepUntilDate:[NSDate distantFuture]]; // 睡到遥远的未来45    // [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; //线程睡到从现在开始后的2秒为止46   }47 48   if ([huThread isCancelled]) {49    NSLog(@"good bye2");50    return;51   }52 53   if (i == 20) {54    //清空资源55    CGPathRelease(path);56 57    //在调用下面方法之前,必须清空资源 非正常死亡--**(退出线程)58    [NSThread exit];59   }60 61   if ([huThread isCancelled]) {62    NSLog(@"good bye3");63    return;64   }65   NSLog(@"%d", i);66  }67 } //--->huThread死亡状态 (正常死亡状态)68 69 - (void)didReceiveMemoryWarning {70  [super didReceiveMemoryWarning];71  // Dispose of any resources that can be recreated.72 }73 74 @end

 

4.2-4.1 多线程安全隐患 - 资源共享/抢夺

(1) 起因 : 
 
资源共享概念 : 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
 
主要是因为多条线程,对`同一资源同时操作`,导致的问题
(2) 举例 : 比如多个线程访问同一个对象、同一个变量、同一个文件
(3) 结果 : 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题 (资源强夺)
(4) 解决方案 : 互斥锁 / 同步锁 

代码示例 :  (卖票案例)
多线程开发的复杂度相对较高,在开发时可以按照以下套路编写代码:
  • 首先确保单个线程执行正确
  • 添加线程
 1 #import "ViewController.h" 2  3 @interface ViewController () 4 @property(nonatomic, strong) NSThread *thread01; // 售票员01 5 @property(nonatomic, strong) NSThread *thread02; // 售票员02 6 @property(nonatomic, strong) NSThread *thread03; // 售票员03 7  8 @property(nonatomic, assign) NSInteger ticketCount; //票的总数 9 @end10 11 @implementation ViewController12 13 - (void)viewDidLoad {14  [super viewDidLoad];15  // Do any additional setup after loading the view, typically from a nib.16 17  self.ticketCount = 10;18 19  //创建线程20  self.thread01 = [[NSThread alloc] initWithTarget:self21                      selector:@selector(saleTicket)22                       object:nil];23  self.thread01.name = @"售票员01";24 25  self.thread02 = [[NSThread alloc] initWithTarget:self26                      selector:@selector(saleTicket)27                       object:nil];28  self.thread02.name = @"售票员02";29 30  self.thread03 = [[NSThread alloc] initWithTarget:self31                      selector:@selector(saleTicket)32                       object:nil];33  self.thread03.name = @"售票员03";34 }35 36 // 开启线程37 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {38  [self.thread01 start];39  [self.thread02 start];40  [self.thread03 start];41 }42 43 // 卖票44 - (void)saleTicket {45 46  while (1) {47 48   @synchronized(self) { //互斥锁(控制器做锁对象)49    // 先取出总数50    NSInteger count = self.ticketCount;51 52    // 判断还有没有余票53    if (count > 0) {54     self.ticketCount = count - 1;55     NSLog(@"%@卖了一张票,还剩下%zd张", [NSThread currentThread].name,56        self.ticketCount);57    } else {58     NSLog(@"票已经卖完了");59     break;60    }61   }62  }63 }64 65 - (void)didReceiveMemoryWarning {66  [super didReceiveMemoryWarning];67  // Dispose of any resources that can be recreated.68 }69 70 @end


打印结果:2016-03-17 19:37:27.429 线程安全[3386:472835] 售票员02卖了一张票,还剩下9张2016-03-17 19:37:27.430 线程安全[3386:472836] 售票员03卖了一张票,还剩下8张2016-03-17 19:37:27.430 线程安全[3386:472834] 售票员01卖了一张票,还剩下7张2016-03-17 19:37:27.430 线程安全[3386:472835] 售票员02卖了一张票,还剩下6张2016-03-17 19:37:27.430 线程安全[3386:472836] 售票员03卖了一张票,还剩下5张2016-03-17 19:37:27.430 线程安全[3386:472834] 售票员01卖了一张票,还剩下4张2016-03-17 19:37:27.431 线程安全[3386:472835] 售票员02卖了一张票,还剩下3张2016-03-17 19:37:27.431 线程安全[3386:472836] 售票员03卖了一张票,还剩下2张2016-03-17 19:37:27.431 线程安全[3386:472834] 售票员01卖了一张票,还剩下1张2016-03-17 19:37:27.431 线程安全[3386:472835] 售票员02卖了一张票,还剩下0张2016-03-17 19:37:27.432 线程安全[3386:472836] 票已经卖完了2016-03-17 19:37:27.434 线程安全[3386:472834] 票已经卖完了2016-03-17 19:37:27.434 线程安全[3386:472835] 票已经卖完了

 

4.2-4.2 安全隐患解决 – 互斥锁 / 同步锁

互斥锁使用技术 : 线程同步
概念:多条线程按顺序地执行任务
引申 : 互斥锁,就是使用了线程同步技术

 
互斥锁使用格式 :
1 //锁对象为能够加锁的任意 NSObject 对象2 //锁对象一定要保证所有的线程都能够访问3 //如果代码中只有一个地方需要加锁,大多都使用self,这样可以避免单独再创建一个锁对象4 5 @synchronized(锁对象) {6   //需要锁定的代码7 }



注意:
  • 锁定1份代码只用1把锁,用多把锁是无效的
  • 保证锁内的代码,同一时间,只有一条线程能够执行!
  • 互斥锁的锁定范围,应该尽量小,锁定范围越大,效率越差!
  • 速记 :  [[NSUserDefaults standardUserDefaults] synchronize];
 
互斥锁的使用前提:
多条线程抢夺同一块资源

 
互斥锁的优缺点 :
优点:能有效防止因多线程抢夺资源造成的数据安全问题 , 能保证数据的准确性
缺点:需要消耗大量的CPU资源 , 可能会导致执行速度变慢
 

 
4.2-4.3 原子属性/非原子属性
原子和非原子属性:
(默认) atomic原子属性为setter方法加锁线程安全,需要消耗大量的资源
(推荐) nonatomic非原子属性不会为setter方法加锁非线程安全,适合内存小的移动设备
 
atomic加锁原理:
1 @property (assign, atomic) int age;2 - (void)setAge:(int)age3 {4   @synchronized(self) {5     _age = age;6   }7 }

 

 iOS开发的建议:
  • 所有属性都声明为nonatomic
  • 尽量避免多线程抢夺同一块资源
  • 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力
 
 4.2-5.0 线程间通讯和常用方法
线程间通信:
概念 : 在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信
线程间通信的体现 : 
  • 1个线程传递数据给另1个线程
  • 在1个线程中执行完特定任务后,转到另1个线程继续执行任务
 
 
线程间通信常用方法 : 
1 //在主线程执行操作 (从子线程回到主线程)(推荐)2 - (void)performSelectorOnMainThread:(SEL)aSelector3             withObject:(id)arg4            waitUntilDone:(BOOL)wait;5 6 - (void)performSelector:(SEL)aSelector7        onThread:(NSThread *)thr8       withObject:(id)arg9      waitUntilDone:(BOOL)wait;

另外一种线程之间的通信方式:NSPort(端口)


包括的子类:
(1)NSMessagePort;
(2)NSMachPort;
 

 

4.2-5.1 图片下载示例
 1 #import "ViewController.h" 2  3 @interface ViewController () 4 @property(weak, nonatomic) IBOutlet UIImageView *imageView; 5 @end 6  7 @implementation ViewController 8  9 - (void)viewDidLoad {10  [super viewDidLoad];11  // Do any additional setup after loading the view, typically from a nib.12 }13 14 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {15  // 开启子线程(在子线程中调用download方法下载图片)16  [self performSelectorInBackground:@selector(download) withObject:nil];17 }18 19 #pragma mark - 图片下载20 - (void)download {21  // 1.图片的网络路径22  NSURL *url =23    [NSURL URLWithString:@"http://www.5068.com/u/faceimg/20140804114111.jpg"];24 25  // 2.下载图片并把图片转换为二进制的数据(耗时操作)26  NSData *data = [NSData dataWithContentsOfURL:url];27 28  // 3.生成图片(把数据转换成图片)29  UIImage *image = [UIImage imageWithData:data];30 31  // 4.回到主线程中设置图片32  // 方法1:33  [self.imageView34    performSelector:@selector(setImage:)35        onThread:[NSThread mainThread]36       withObject:image37     waitUntilDone:NO]; //是否等到@selector的方法完成后再往下执行,NO表示否38 39  //方法2:40  [self.imageView performSelectorOnMainThread:@selector(setImage:)41                   withObject:image42                 waitUntilDone:NO];43  // 方法3:代码一44  [self performSelectorOnMainThread:@selector(showImage:)45              withObject:image46            waitUntilDone:NO];47 }48 49 // 方法3:代码二50 - (void)showImage:(UIImage *)image {51  self.imageView.image = image; //设置显示图片52 }53 54 #pragma mark - 测试图片下载时间 方法1:NSDate55 - (void)download1 {56  // 1.图片的网络路径57  NSURL *url =58    [NSURL URLWithString:@"http://www.5068.com/u/faceimg/20140804114111.jpg"];59 60  NSDate *begin = [NSDate date]; //开始时间61 62  // 2.根据图片的网络路径去下载图片数据63  NSData *data = [NSData dataWithContentsOfURL:url];64 65  NSDate *end = [NSDate date]; //结束时间66 67  NSLog(@"%f", [end timeIntervalSinceDate:begin]); // 时间间隔 = 开始-结束68 69  // 3.显示图片70  self.imageView.image = [UIImage imageWithData:data];71 }72 73 #pragma mark - 测试图片下载时间 方法2:CFTimeInterval74 - (void)download2 {75  // 1.图片的网络路径76  NSURL *url =77    [NSURL URLWithString:@"http://www.5068.com/u/faceimg/20140804114111.jpg"];78 79  CFTimeInterval begin = CFAbsoluteTimeGetCurrent(); // 开始时间80 81  // 2.根据图片的网络路径去下载图片数据82  NSData *data = [NSData dataWithContentsOfURL:url];83 84  CFTimeInterval end = CFAbsoluteTimeGetCurrent(); //结束时间85 86  NSLog(@"%f", end - begin); // 时间间隔 = 开始-结束87 88  // 3.显示图片89  self.imageView.image = [UIImage imageWithData:data];90 }91 92 - (void)didReceiveMemoryWarning {93  [super didReceiveMemoryWarning];94  // Dispose of any resources that can be recreated.95 }96 97 @end

4.1/4.2 多线程进阶篇上(Pthread  NSThread)

 

 


 

如果你觉得本篇文章对你有所帮助,请点击右下部“推荐”,^_^
 
 
作者:蓝田(Loto)
出处:http://www.cnblogs.com/shorfng/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
 


如有疑问,请发送邮件至 shorfng@126.com 联系我。
 

 


 



原标题:4.1/4.2 多线程进阶篇上(Pthread NSThread)

关键词:线程

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

易达恒通供应链:https://www.goluckyvip.com/tag/47626.html
易达全球:https://www.goluckyvip.com/tag/47627.html
易达全球物流:https://www.goluckyvip.com/tag/47628.html
易达天下:https://www.goluckyvip.com/tag/47629.html
次日达广告:https://www.goluckyvip.com/tag/4763.html
易达天下供应链:https://www.goluckyvip.com/tag/47630.html
17万件月销量!Wildplus Beauty香薰身体精华油大热趋势 :https://www.kjdsnews.com/a/1836305.html
17万件月销量!Wildplus Beauty香薰身体精华油大热趋势 :https://www.goluckyvip.com/news/186214.html
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流