其他分享
首页 > 其他分享> > 多线程

多线程

作者:互联网

多线程

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