//
多线程
// 单线程 : 在应用程序启动的时候 , 会自动创建一个线程。该线程为主线程。只有一个主线程的应用程序为单线程应用程序。在主线程的代码会顺序执行。
// 单线程程序的问题 : 在程序运行的过程中难免会需要请求数据、解析数据等等很多耗时的任务。这时如果把所有的任务都放在主线程中执行会造成程序的假死现象 , 用户交互不好。
// 为了提高用户的交互性 , 可以采用多线程。
// 多线程 : 即程序中不仅仅有一个主线程。这些子线程主要用来进行一些复杂的工作。
// 使用多线程注意事项 : 不管开辟的多少个子线程 , 最终 UI 界面的刷新和展示 ( 即所有对 UI 的操作 ) 都需要回到主线程中执行。 ==>> 问题 : 如何从子线程回到主线程 ?
// 使用多线程的优缺点 :
// 优点 : 可以防止主线程阻塞 , 提用户交互性。
// 缺点 : 1. 在开辟子线程的时候需要耗费一定的资源。
// 单线程 : 在应用程序启动的时候 , 会自动创建一个线程。该线程为主线程。只有一个主线程的应用程序为单线程应用程序。在主线程的代码会顺序执行。
// 单线程程序的问题 : 在程序运行的过程中难免会需要请求数据、解析数据等等很多耗时的任务。这时如果把所有的任务都放在主线程中执行会造成程序的假死现象 , 用户交互不好。
// 为了提高用户的交互性 , 可以采用多线程。
// 多线程 : 即程序中不仅仅有一个主线程。这些子线程主要用来进行一些复杂的工作。
// 使用多线程注意事项 : 不管开辟的多少个子线程 , 最终 UI 界面的刷新和展示 ( 即所有对 UI 的操作 ) 都需要回到主线程中执行。 ==>> 问题 : 如何从子线程回到主线程 ?
// 使用多线程的优缺点 :
// 优点 : 可以防止主线程阻塞 , 提用户交互性。
// 缺点 : 1. 在开辟子线程的时候需要耗费一定的资源。
// 2.
不管开辟了多少个子线程最终要回到主线程
,
这时也需要耗
费
CPU
性能。
// 3.
子线程开辟过多少会造成代码可读性比较差。
// 问题一 : 如何从子线程回到主线程 :
// 1.NSObject 对应的方法 :performSelectorOnMainThread
// 问题一 : 如何从子线程回到主线程 :
// 1.NSObject 对应的方法 :performSelectorOnMainThread
// 2.dispatch_async(dispatch_get_main_queue(), ^{});
/********************
第一种方式
NSThread********************/
// 获取当前的线程 (thread)
NSLog ( @"mainThread:%@" , [ NSThread currentThread ]);
// initWithTarget:selector:object: 参数含义如下 :
// 1. 目标
// 2. 方法名
// 3. 方法对于的参数
// 在使用下列方法时需要注意该方法不会自动开辟子线程执行方法 , 需要手动调用 start 开启。 ( 开辟子线程 )
// NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(calculate:) object:self.NumberTF.text];
// // 开启
// [thread1 start];
// 这个方法使用会立即跳到子线程 , 不需要手动 Start
// 获取当前的线程 (thread)
NSLog ( @"mainThread:%@" , [ NSThread currentThread ]);
// initWithTarget:selector:object: 参数含义如下 :
// 1. 目标
// 2. 方法名
// 3. 方法对于的参数
// 在使用下列方法时需要注意该方法不会自动开辟子线程执行方法 , 需要手动调用 start 开启。 ( 开辟子线程 )
// NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(calculate:) object:self.NumberTF.text];
// // 开启
// [thread1 start];
// 这个方法使用会立即跳到子线程 , 不需要手动 Start
[NSThread detachNewThreadSelector:@selector(calculate:) toTarget:self withObject:self.NumberTF.text];
//
在子线程里加载图片
,
然后回到主线程显示图片
/*************** 方式二使用 NSObject*****************/
NSURL *url = [ NSURL URLWithString : kURL ];
#pragma mark - 该方法是 NSObject 的方法 , 凡是继承 NSObject 的类都可以使用该方法。该方法调用几次就会开辟几个子线程。 ==> 问题 : 如果不同线程争夺同一个资源 , 咋办 ?
// @synchronized(<#token#>) { // 线程锁方法一
// <#statements#>
// }
/*************** 方式二使用 NSObject*****************/
NSURL *url = [ NSURL URLWithString : kURL ];
#pragma mark - 该方法是 NSObject 的方法 , 凡是继承 NSObject 的类都可以使用该方法。该方法调用几次就会开辟几个子线程。 ==> 问题 : 如果不同线程争夺同一个资源 , 咋办 ?
// @synchronized(<#token#>) { // 线程锁方法一
// <#statements#>
// }
[self performSelectorInBackground:@selector(downloadMyImage:) withObject:url]; // 开启子线程
/************
方式三
使用
NSOperationQueue*************/
// NSOperationQueue: 操作队列。用来管理一组操作 (NSOperation) 。
// 注意事项 :NSOperationQueue 开辟线程的个数是由系统决定的。不是由操作的个数决定的。 ( ⭐️⭐️⭐️⭐️⭐️ ) ==> 面试题 !!!
// 创建两个 NSOperation(NSBlockOperation\ NSInvocation)
NSBlockOperation *blockOp = [ NSBlockOperation blockOperationWithBlock :^{
NSLog ( @"blockThread:%@" , [ NSThread currentThread ]);
}];
NSInvocationOperation *invocationOp = [[ NSInvocationOperation alloc ] initWithTarget : self selector : @selector (calculate:) object : @"50" ];
NSOperationQueue *queue = [[ NSOperationQueue alloc ] init ];
#pragma mark - 下列方法中参数的含义 :
// 1. 操作对应的数组
// 2.BOOL: 如果给的是 YES, 那么会阻塞当前线程 , 直到操作队列里面所有的操作被执行完 , 当前线程才不会被阻塞。
// 如果给的是 NO, 那么不会阻塞当前线程。
// 可能会产生的问题 : 如果给的是 YES, 而且卡死的是主线程。那么在操作中如果需要对 UI 进行操作需要返回到主线程中去执行代码。这时程序就会出现死锁 ( 一直出不来 , 按键也一直不能再次点击 ) 。
// 设置最大并发数为 1.( 当前在操作队列中正在运行的任务只有一个。与线程个数无关。 )
// 如果设置最大并发数为 1, 那么可以实现线程同步。
// 线程同步 : 只有当一个线程中的任务执行完毕之后 , 才能执行另外一个线程的任务。
queue. maxConcurrentOperationCount = 1 ;
// NSOperationQueue: 操作队列。用来管理一组操作 (NSOperation) 。
// 注意事项 :NSOperationQueue 开辟线程的个数是由系统决定的。不是由操作的个数决定的。 ( ⭐️⭐️⭐️⭐️⭐️ ) ==> 面试题 !!!
// 创建两个 NSOperation(NSBlockOperation\ NSInvocation)
NSBlockOperation *blockOp = [ NSBlockOperation blockOperationWithBlock :^{
NSLog ( @"blockThread:%@" , [ NSThread currentThread ]);
}];
NSInvocationOperation *invocationOp = [[ NSInvocationOperation alloc ] initWithTarget : self selector : @selector (calculate:) object : @"50" ];
NSOperationQueue *queue = [[ NSOperationQueue alloc ] init ];
#pragma mark - 下列方法中参数的含义 :
// 1. 操作对应的数组
// 2.BOOL: 如果给的是 YES, 那么会阻塞当前线程 , 直到操作队列里面所有的操作被执行完 , 当前线程才不会被阻塞。
// 如果给的是 NO, 那么不会阻塞当前线程。
// 可能会产生的问题 : 如果给的是 YES, 而且卡死的是主线程。那么在操作中如果需要对 UI 进行操作需要返回到主线程中去执行代码。这时程序就会出现死锁 ( 一直出不来 , 按键也一直不能再次点击 ) 。
// 设置最大并发数为 1.( 当前在操作队列中正在运行的任务只有一个。与线程个数无关。 )
// 如果设置最大并发数为 1, 那么可以实现线程同步。
// 线程同步 : 只有当一个线程中的任务执行完毕之后 , 才能执行另外一个线程的任务。
queue. maxConcurrentOperationCount = 1 ;
[queue addOperations:@[blockOp, invocationOp] waitUntilFinished:NO];
/*************************
方式四
GCD************************/
// GCD 的核心是分发队列。
// 分发队列有两种形式 :
// 串行队列 : 一次只能执行一个任务。如果有多个串行队列那么每个队列中的任务都是同步执行的 , 但是队列之间可以并发执行 ( 互不干扰 ) 。
// 并行队列 : 并发的执行多个任务。但是 FIFO.( 只支持先进 , 并不一定先出。 )
- ( IBAction )serialQueue:( id )sender {
// 串行队列有两种形式 :
// 1. 系统提供的 :dispatch_async(dispatch_get_main_queue: 主队列 ==>> 在主线程中。
dispatch_async ( dispatch_get_main_queue (), ^{
NSLog ( @" 下载图片 :%@" , [ NSThread currentThread ]);
});
// 2. 自己创建的 ( 自己创建的会再子线程 )
dispatch_queue_t queue = dispatch_queue_create ( "com.fy.queue1" , DISPATCH_QUEUE_SERIAL );
dispatch_async (queue, ^{
NSLog ( @"thread:%@" , [ NSThread currentThread ]); // 子线程中
});
// 串行是顺序执行
dispatch_async (queue, ^{
NSLog ( @" 小米出生了 " );
});
dispatch_async (queue, ^{
NSLog ( @" 小米上学了 " );
});
dispatch_async (queue, ^{
NSLog ( @" 小米结婚了 " );
});
dispatch_async (queue, ^{
NSLog ( @" 小米有宝宝了 " );
});
}
- ( IBAction )concurrentQueue:( id )sender {
// 并行队列有两种 :
// 1. 系统提供的 ( 也是子线程 )
dispatch_async ( dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 ), ^{
NSLog ( @"======:%@" , [ NSThread currentThread ]);
});
// 2. 自己创建的
dispatch_queue_t concurrentQueue = dispatch_queue_create ( "com.fy.concurrentQueue" , DISPATCH_QUEUE_CONCURRENT );
dispatch_async (concurrentQueue, ^{
NSLog ( @"thread:%@" , [ NSThread currentThread ]); // 子线程中
});
// 并行队列与顺序没有关系 , 是无序的
dispatch_async (concurrentQueue, ^{
NSLog ( @" 小米出生了 " );
});
dispatch_async (concurrentQueue, ^{
NSLog ( @" 小米上学了 " );
});
dispatch_async (concurrentQueue, ^{
NSLog ( @" 小米结婚了 " );
});
dispatch_async (concurrentQueue, ^{
NSLog ( @" 小米有宝宝了 " );
});
}
// 使用 GCD
- ( IBAction )useGCD:( id )sender {
dispatch_queue_t myQueue = dispatch_queue_create ( "com.fy.myQueue" , DISPATCH_QUEUE_SERIAL );
dispatch_async (myQueue, ^{
UIImage *image = [ UIImage imageWithData :[ NSData dataWithContentsOfURL :[ NSURL URLWithString : kImage ]]];
dispatch_async ( dispatch_get_main_queue (), ^{
self . imageView . image = image;
});
});
// GCD 的核心是分发队列。
// 分发队列有两种形式 :
// 串行队列 : 一次只能执行一个任务。如果有多个串行队列那么每个队列中的任务都是同步执行的 , 但是队列之间可以并发执行 ( 互不干扰 ) 。
// 并行队列 : 并发的执行多个任务。但是 FIFO.( 只支持先进 , 并不一定先出。 )
- ( IBAction )serialQueue:( id )sender {
// 串行队列有两种形式 :
// 1. 系统提供的 :dispatch_async(dispatch_get_main_queue: 主队列 ==>> 在主线程中。
dispatch_async ( dispatch_get_main_queue (), ^{
NSLog ( @" 下载图片 :%@" , [ NSThread currentThread ]);
});
// 2. 自己创建的 ( 自己创建的会再子线程 )
dispatch_queue_t queue = dispatch_queue_create ( "com.fy.queue1" , DISPATCH_QUEUE_SERIAL );
dispatch_async (queue, ^{
NSLog ( @"thread:%@" , [ NSThread currentThread ]); // 子线程中
});
// 串行是顺序执行
dispatch_async (queue, ^{
NSLog ( @" 小米出生了 " );
});
dispatch_async (queue, ^{
NSLog ( @" 小米上学了 " );
});
dispatch_async (queue, ^{
NSLog ( @" 小米结婚了 " );
});
dispatch_async (queue, ^{
NSLog ( @" 小米有宝宝了 " );
});
}
- ( IBAction )concurrentQueue:( id )sender {
// 并行队列有两种 :
// 1. 系统提供的 ( 也是子线程 )
dispatch_async ( dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 ), ^{
NSLog ( @"======:%@" , [ NSThread currentThread ]);
});
// 2. 自己创建的
dispatch_queue_t concurrentQueue = dispatch_queue_create ( "com.fy.concurrentQueue" , DISPATCH_QUEUE_CONCURRENT );
dispatch_async (concurrentQueue, ^{
NSLog ( @"thread:%@" , [ NSThread currentThread ]); // 子线程中
});
// 并行队列与顺序没有关系 , 是无序的
dispatch_async (concurrentQueue, ^{
NSLog ( @" 小米出生了 " );
});
dispatch_async (concurrentQueue, ^{
NSLog ( @" 小米上学了 " );
});
dispatch_async (concurrentQueue, ^{
NSLog ( @" 小米结婚了 " );
});
dispatch_async (concurrentQueue, ^{
NSLog ( @" 小米有宝宝了 " );
});
}
// 使用 GCD
- ( IBAction )useGCD:( id )sender {
dispatch_queue_t myQueue = dispatch_queue_create ( "com.fy.myQueue" , DISPATCH_QUEUE_SERIAL );
dispatch_async (myQueue, ^{
UIImage *image = [ UIImage imageWithData :[ NSData dataWithContentsOfURL :[ NSURL URLWithString : kImage ]]];
dispatch_async ( dispatch_get_main_queue (), ^{
self . imageView . image = image;
});
});
}