多线程
作者:互联网
多线程
1. 进程和线程
一个应用程序可以对应多个进程
每个进程中至少有一个线程
进程中的线程共享该进程的资源
2. 线程的串行
线程执行任务的方式 -- 串行(任务和任务之间有执行顺序,即多个任务一个一个地按顺序执行,一个线程同时只能执行一个任务)
3. 多线程的优缺点
单个进程中的每条线程可以并行执行任务
单CPU
同一时间CPU只能处理一条线程,即只有一条线程在工作
多线程并发执行,实则是CPU快速地在线程之间调度切换
优点
适当提高程序的执行效率
适当提高资源的利用率
缺点
空间开销:内核数据结构,栈空间
时间开销:约90ms的创建时间
性能降低:在开启大量线程时降低程序性能,同时CPU调度线程时开销更大
程序设计:线程之间通信,多线程数据共享(同一数据被多个线程共享导致数据安全问题)更加复杂
4. 多线程的安全隐患
资源共享问题
一块资源可能会同时被多个线程共享,从而引发数据错乱和数据安全问题。
安全隐患解决
互斥锁
注意:互斥锁必须是全局唯一的,且只能用同一把锁为一块区域加锁,用多把锁则无效
优点:有效防止因多线程抢夺资源造成的数据安全问题,实现“线程同步”
缺点:需要消耗大量CPU资源
代码演示
// ViewController.m
//售票员卖票demo
@interface ViewController()
@property(nonatomic, strong) NSThread *threadA;
@property(nonatomic, strong) NSThread *threadB;
@property(nonatomic, strong) NSThread *threadC;
@end
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.ticketCount = 100;
//开启三个售票员线程
self.threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
self.threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
self.threadC = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
//线程属性设置
self.threadA.name = @"售票员A";
self.threadB.name = @"售票员B";
self.threadC.name = @"售票员C";
//启动线程
[self.threadA start];
[self.threadB start];
[self.threadC start];
}
- (void)saleTicket {
while(1) {
@synchronized (self) {
//对于每一次卖票的模块上锁,保证同一时间只有一位售票员在卖票
if(self.ticketCount > 0) {
//卖出去一张票
for(int i = 1; i <= 100000; i++); //增加程序执行时间,方便观察结果
NSLog(@"%@卖出去一张,还剩下%zd张", [NSThread currentThread].name, self.ticketCount - 1);
self.ticketCount--;
} else {
NSLog(@"票已售罄");
break;
}
}
}
}
@end
5.线程间通信
A线程专递数据给B线程
A线程执行完特定任务后,转到B线程继续执行任务
代码演示
//网络图片下载Demo -- NSThread实现
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong) UIImageView *imageView;
@property(nonatomic, strong) NSThread *threadA;
@end
@implementation ViewController
//耗时操作要用子线程进行
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//分离一条子线程去执行download方法
[NSThread detachNewThreadSelector:@selector(download) toTarget:self withObject:nil];
}
- (void)download {
//确定图片URL
NSURL *url = [NSURL URLWithString:@"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fa1.att.hudong.com%2F62%2F02%2F01300542526392139955025309984.jpg&refer=http%3A%2F%2Fa1.att.hudong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1613890070&t=9b041b83f4ddfe8c165547120f5aa589"];
//根据URL下载图片二进制数据到本地
NSData *imageData = [NSData dataWithContentsOfURL:url];
//图片格式转换
UIImage *image = [UIImage imageWithData:imageData];
//回到主线程显示UI的三种方法
//方法1
/*
参数1:回到主线程要调用的方法
参数2:前面方法需要的参数
参数3:当前方法剩余部分的执行是否等待参数1方法执行完毕
*/
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
//方法2 -- 主动选择执行线程为主线程
[self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
//方法3 -- 直接用self.imageView调用主线程方法
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
}
- (void)showImage:(UIImage *)image {
self.imageView.image = image;
}
@end
6.多线程在iOS开发中的应用
1). 主线程与子线程
主线程
一个iOS程序运行后,默认开启一条“主线程”或“UI线程”
主线程的作用
显示/刷新UI界面
处理UI事件(点击,滚动,拖拽等)
主线程的使用注意
凡是和UI相关的操作必须在主线程中执行
不将耗时操作放在主线程中 -- 会卡住主线程,严重影响UI流畅度,降低用户体验
子线程
用来执行耗时操作
代码演示
// ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
//1.获得主线程
NSThread *mainThread = [NSThread mainThread];
NSLog(@"%@", mainThread);
//2.获得当前线程
NSThread *currentThread = [NSThread currentThread];
NSLog(@"%@", currentThread);
//3.判断主线程
//3.1 打印该线程,number == 1 则为主线程
//3.2 类方法
BOOL isMainThreadA = [NSThread isMainThread];
//3.3 对象方法
BOOL isMainThreadB = [currentThread isMainThread];
NSLog(@"%zd---%zd", isMainThreadA, isMainThreadB);
}
2). 原子性与非原子性
atomic 原子属性
为setter方法加锁(默认为atomic)
线程安全,消耗大量资源
nonatomic 非原子属性
不会为setter方法加锁
非线程安全,适合小内存移动设备
5.iOS的多线程实现方案
技术方案 | 简介 | 语言 | 线程生命周期 | 使用频率 |
---|---|---|---|---|
pthread | 1.通用的多线程API 2.适用于Unix/Linux/Windows等系统 3.跨平台/可移植 4.使用难度较大 |
C语言 | 程序员管理 | 少 |
NSThread | 1.更加面向对象 2.简单易用,可直接操作线程对象 |
OC | 程序员管理 | 正常 |
GCD | 1.旨在代替NSThread等线程技术 2.充分利用设备的多核资源 |
C | 自动管理 | 经常使用 |
NSOperation | 1.基于GCD,增加了一些简单功能 2.更加面向对象 |
OC | 自动管理 | 经常使用 |
1.pthread
// ViewController.m
#import <pthread.h>
- (void)viewDidLoad {
[super viewDidLoad];
//1.创建线程对象
pthread_t thread;
//2.线程创建函数
//1).线程对象 -- 地址传递
//2).线程的属性
//3).指向函数的指针
//4).前面函数要接收的参数 -- NULL
pthread_create(&thread, NULL, func, NULL);
//3.判断两条线程是否相等
pthread_equal();
}
void *func(*void) {
NSLog(@"%@-----", [NSThread currentThread]);
return NULL;
}
2.NSThread
1). 线程的创建
// ViewController.m
//线程创建方法1 -- allo + init
- (void)createThread1 {
/*
第一个参数:目标对象 self
第二个参数:方法选择器 调用的方法
第三个参数:前面调用方法需要传递的参数 nil
*/
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"test"];
//启动线程
[thread start];
//设置线程属性
thread.name = @"线程A";
[thread setName:@"线程A"];
//设置线程优先级 -- 优先级大小(0.0 ~ 1.0), 默认为0.5
thread.threadPriority = 1.0;
//线程生命周期 -- 当任务执行完成之后被释放
}
//线程创建方法2 -- 无法获取创建的线程对象
- (void)createThread2 {
//分离子线程方法
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"通过分离子线程创建新线程"];
}
//线程创建方法3-- 无法获取创建的线程对象
- (void)createThread3 {
//开启后台线程方法
[self performSelectorInBackground:@selector(run:) withObject:@"通过开启后台线程创建新线程"];
}
- (void)test:(NSString *)para {
NSLog(@"-----test-----%@", [NSThread currentThread]);
}
2). 线程的状态
// ViewController.m
- (void)createThread1 {
//新建状态
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"test"];
//就绪或运行状态
[thread start];
}
- (void)test:(NSString *)para {
NSLog(@"-----test-----%@", [NSThread currentThread]);
//阻塞状态
[NSThread sleepFortimeinterval:2.0];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
//死亡状态
[NSThread exit];
}
3. GCD -- Grand Central Dispatch
强大的中枢调度器
纯C语言实现
解决多核的并行运算,能够自动利用更多的CPU内核,自动管理线程的生命周期
1).核心概念
任务 -- 执行什么操作
队列 -- 用来调度任务
2).GCD的使用
定制任务
将任务添加到队列 -- 先进先出地自动取出任务放到对应线程中执行
3).执行任务
同步执行 -- 只能在当前线程执行任务,不能开启新线程
异步执行 -- 可以在新线程中执行任务,可以开启新线程
4). 队列类型
并发队列 -- 可以让多个任务并发执行 (自动开启多个线程同时执行任务) -- 并发功能只在异步函数下生效
串行队列 -- 让任务一个接一个地执行
代码演示
//同步函数 异步函数 并发队列 串行队列
#import "ViewController.h"
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self asyncConcurrent];
[self asyncSerial];
[self syncConcurrent];
[self syncSerial];
}
//异步函数 + 并发队列 -- 会开启多条线程,队列中的任务异步执行
- (void)asyncConcurrent {
//1.创建队列 -- 并发队列
/*
参数1:C语言字符串 -- 队列标签
参数2:队列类型 DISPATCH_QUEUE_CONCURRENT -- 并发
DISPATCH_QUEUE_SERIAL -- 串行
*/
dispatch_queue_t queue = dispatch_queue_create("label1", DISPATCH_QUEUE_CONCURRENT);
//2.封装任务并添加到队列中 -- 异步函数
/*
参数1:队列名
参数2:要执行的任务
*/
//测试是否开启了多条线程
dispatch_async(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
}
//异步函数 + 串行队列 -- 会开启一条线程,队列中的任务串行执行
- (void)asyncSerial {
//1.创建队列 -- 串行队列
dispatch_queue_t queue = dispatch_queue_create("label2", DISPATCH_QUEUE_SERIAL);
//测试是否开启了多条线程
dispatch_async(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
}
//同步函数 + 并行队列 -- 不会开启线程
- (void)syncConcurrent {
//1.创建并发队列
dispatch_queue_t queue = dispatch_queue_create("label3", DISPATCH_QUEUE_CONCURRENT);
//测试是否开启了多条线程
dispatch_sync(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
}
//同步函数 + 串行队列 -- 不会开启线程
- (void)syncSerial {
//1.创建并发队列
dispatch_queue_t queue = dispatch_queue_create("label4", DISPATCH_QUEUE_SERIAL);
//测试是否开启了多条线程
dispatch_sync(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
});
}
@end
标签:--,self,queue,线程,NSThread,多线程,void 来源: https://www.cnblogs.com/blackcrystal/p/14314461.html