• 多线程的使用


     

    通过NSThread方式开辟线程三种方式

    //1.alloc init,手动开启|能够对线程进行更加详细的设置
    -(void)createNewThread1
    {
        //1.创建线程
        /*
         第一个参数:目标对象  self
         第二个参数:要调用的方法的名称
         第三个参数:要调用方法需要传递的参数<最多传递一个参数,不传nil
         */
    
       MSHThread *threadA =  [[MSHThread alloc]initWithTarget:self selector:@selector(task) object:nil];
    
        //设置线程的属性
        threadA.name = @"线程A";         //设置线程的名称
        threadA.threadPriority = 1.0;   //设置线程的优先级,优先级取值范围为0.0~1.0 最高为1.0
        
        //2.启动线程
        [threadA start];
        self.threadA = threadA;
        
        
        /////////在创建两条线程////////////
        
        MSHThread *threadB =  [[MSHThread alloc]initWithTarget:self selector:@selector(task) object:nil];
        threadB.name = @"线程B";
        [threadB start];
        
        MSHThread *threadC =  [[MSHThread alloc]initWithTarget:self selector:@selector(task) object:nil];
        threadC.name = @"线程C";
        threadC.threadPriority = 0.1;
        [threadC start];
    }
    分离方式创建线程
    //2.分离出一条新的线程,自动开启|不能够对线程进行更加详细的设置
    -(void)createNewThread2
    {
        //分离出一条新的线程
        /*
         第一个参数:要调用的方法
         第二个参数:目标对象  self
         第三个参数:要调用方法需要传递的参数
         */
        [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"分离出一条新的线程"];
    }
    
    //3.开启一条后台线程,自动开启|不能够对线程进行更加详细的设置
    -(void)createNewThread3
    {
        //开启一条后台线程 开后台线程的方式
        [self performSelectorInBackground:@selector(run:) withObject:@"开启一条后台线程"];
    }
    
    //线程生命周期:当任务执行完毕之后,线程对象会被销毁
    -(void)task
    {
      耗时操作放进线程
        for (NSInteger i =0; i<100; i++) {
             NSLog(@"%zd---task----%@",i,[NSThread currentThread].name);
        }
    }
    
    -(void)run:(NSString *)param
    {
        NSLog(@"run---%@---%@",[NSThread currentThread],param);
    }

    线程的状态接创建跟跟销毁

    启动线程
    - (void)start; 
    // 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态
    
    阻塞(暂停)线程
    + (void)sleepUntilDate:(NSDate *)date;
    + (void)sleepForTimeInterval:(NSTimeInterval)ti;
    // 进入阻塞状态
    
    强制停止线程
    + (void)exit;
    // 进入死亡状态
    
    注意:一旦线程停止(死亡)了,就不能再次开启任务

    线程安全

    #import "ViewController.h"
    
    @interface ViewController ()
    /** 售票员A*/
    @property (nonatomic ,strong)NSThread *threadA;
    /** 售票员B*/
    @property (nonatomic ,strong)NSThread *threadB;
    /** 售票员C*/
    @property (nonatomic ,strong)NSThread *threadC;
    /** 总票数*/
    @property (nonatomic ,assign)NSInteger totalTickets;
    
    @end
    
    @implementation ViewController
    
    -(void)viewDidLoad
    {
         self.threadA = [[NSThread alloc]initWithTarget:self selector:@selector(sale) object:nil];
        self.threadA.name = @"售票员A";
        
         self.threadB = [[NSThread alloc]initWithTarget:self selector:@selector(sale) object:nil];
        self.threadB.name = @"售票员B";
        
         self.threadC = [[NSThread alloc]initWithTarget:self selector:@selector(sale) object:nil];
        self.threadC.name = @"售票员C";
        
        //设置100
        self.totalTickets = 100;
    
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        [self.threadA start];
        [self.threadB start];
        
        [self.threadC start];
        
    }
    
    -(void)sale
    {
        //int i = 0;
        //查看余票的数量,如有有name就卖出去一张,如果没有就告诉用户今年别回家了
        while (1) {
    // 多条线程访问一个资源,此处加锁 @synchronized(self) {
    //锁对象:要求时唯一的 NSInteger count = self.totalTickets; if (count >0) { [NSThread sleepForTimeInterval:0.01]; self.totalTickets = count - 1; NSLog(@"%@卖出去了一张票,还剩下%zd张票",[NSThread currentThread].name,self.totalTickets); }else { NSLog(@"今年别回家了"); break; } } } }

    多线程的安全隐患
    l资源共享
    
    
    p1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
    
    
    p比如多个线程访问同一个对象、同一个变量、同一个文件
    
    

    l当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题

    解决的方案

    l互斥锁使用格式

    @synchronized(锁对象) { // 需要锁定的代码  }

    注意:锁定1份代码只用1把锁,用多把锁是无效的

    l互斥锁的优缺点
    p优点:能有效防止因多线程抢夺资源造成的数据安全问题
    p缺点:需要消耗大量的CPU资源
    p
    l互斥锁的使用前提:多条线程抢夺同一块资源
    p
    l相关专业术语:线程同步
    p线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
    p互斥锁,就是使用了线程同步技术
     
    原子和非原子属性
    lOC在定义属性时有nonatomic和atomic两种选择
    patomic:原子属性,为setter方法加锁(默认就是atomic)
    pnonatomic:非原子属性,不会为setter方法加锁
     

    线程之间通信

    //演示线程间通信
    -(void)download3
    {
        //1.获得下载图片的url
        在子线程方法中进行耗时操作
        NSURL *url = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/zhidao/pic/item/42166d224f4a20a4884b622491529822730ed0f8.jpg"];
        
        
        //2.下载图片的二进制数据到本地
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        
        //3.把二进制数据转换为image
        UIImage *image = [UIImage imageWithData:imageData];
        
        NSLog(@"下载图片---%@",[NSThread currentThread]);
        
        //4.回到主线程刷新UI
        /*
         第一个参数:要调用的方法
         第二个参数:要传递的参数
         第三个参数:要不要继续等到调用方法执行完毕
         */回主线程的三种方式
        //[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO];
        //[self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
        
        //线程间通信的简便方法
        [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
        
        NSLog(@"---------");
    }
    
    -(void)showImage:(UIImage *)image
    {  刷新UI
    //    [NSThread sleepForTimeInterval:2.0];
         NSLog(@"刷新UI---%@",[NSThread currentThread]);
         self.imageView.image = image;
    }

    通过GDC方式开辟线程(C语言)

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        //[self performSelectorInBackground:@selector(syncMain) withObject:nil];
        [self asyncConcurrent];
    }
    异步:只决定具有开辟线程

    同步:不具备开辟线程的能力

    串行:队列里的任务是有顺序的执行

    并行:队列里的任务是并发执行,没有循序
    注意:并行队列只在异步函数中有效
    //异步函数+并发队列:会开线程,开多条线程,任务并发执行 -(void)asyncConcurrent { //创建队列,保持任务,安排|调度任务 /* 第一个参数:C语言的字符串,设置队列的标签 第二个参数:队列的类型 DISPATCH_QUEUE_SERIAL:串行队列 DISPATCH_QUEUE_CONCURRENT:并发队列 */ dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT); NSLog(@"---start----"); //异步函数,封装任务,添加任务到队列中 //异步函数:不需要等待当前代码执行完毕,就可以执行后面的代码 //同步函数:要等到当前代码执行完毕,才能继续往下执行 /* 第一个参数:队列 第二个参数: */ dispatch_async(queue, ^{ NSLog(@"download 1---%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"download 2---%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"download 3---%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"download 4---%@",[NSThread currentThread]); }); NSLog(@"---end----"); } //异步函数+串行队列:会开线程,1条线程,任务串行执行 -(void)asyncSerial { //创建队列,保持任务,安排|调度任务 /* 第一个参数:C语言的字符串,设置队列的标签 第二个参数:队列的类型 DISPATCH_QUEUE_SERIAL:串行队列 DISPATCH_QUEUE_CONCURRENT:并发队列 */ dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_SERIAL); //异步函数,封装任务,添加任务到队列中 /* 第一个参数:队列 第二个参数: */ dispatch_async(queue, ^{ NSLog(@"download 1---%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"download 2---%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"download 3---%@",[NSThread currentThread]); }); } //同步函数+并发队列:不会开线程,任务串行执行的 -(void)syncConcurrent { //创建队列,保持任务,安排|调度任务 /* 第一个参数:C语言的字符串,设置队列的标签 第二个参数:队列的类型 DISPATCH_QUEUE_SERIAL:串行队列 DISPATCH_QUEUE_CONCURRENT:并发队列 */ // dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT); //获得全局并发队列,默认存在,特殊的并发队列 //第一个参数:队列的优先级 DISPATCH_QUEUE_PRIORITY_DEFAULT == 0 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSLog(@"---start----"); //同步函数,封装任务,添加任务到队列中 /* 第一个参数:队列 第二个参数: */ dispatch_sync(queue, ^{ NSLog(@"download 1---%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"download 2---%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"download 3---%@",[NSThread currentThread]); }); NSLog(@"---end----"); } //同步函数+串行队列:不会开线程,任务串行执行的 -(void)syncSerial { //创建队列,保持任务,安排|调度任务 /* 第一个参数:C语言的字符串,设置队列的标签 第二个参数:队列的类型 DISPATCH_QUEUE_SERIAL:串行队列 DISPATCH_QUEUE_CONCURRENT:并发队列 */ dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_SERIAL); //同步函数,封装任务,添加任务到队列中 /* 第一个参数:队列 第二个参数: */ dispatch_sync(queue, ^{ NSLog(@"download 1---%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"download 2---%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"download 3---%@",[NSThread currentThread]); }); } //异步函数+主队列:不会开线程,任务串行执行的 -(void)asyncMain { //1.获得主队列 //特点:凡是放在主队列中的任务都在主线程中执行 dispatch_queue_t queue = dispatch_get_main_queue(); //2.异步函数,封装任务,添加任务到队列中 dispatch_async(queue, ^{ NSLog(@"download 1---%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"download 2---%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"download 3---%@",[NSThread currentThread]); }); } //同步函数+主队列:死锁 //主队列特点:如果发现主线程当前正在执行代码(任务),那么主队列将不会调度队列里的任务,直到主线程的任务执行完毕 -(void)syncMain { //1.获得主队列 //特点:凡是放在主队列中的任务都在主线程中执行
    // 全局主队列队列
    都会在主线程中执行,有序的,不能用同步函数执行 dispatch_queue_t queue = dispatch_get_main_queue(); NSLog(@"------start----"); //2.同步函数,封装任务,添加任务到队列中 dispatch_sync(queue, ^{ NSLog(@"download 1---%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"download 2---%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"download 3---%@",[NSThread currentThread]); }); NSLog(@"------end----"); }

    CDG中线程的同步

    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        //使用异步函数+并发队列开线程现在图片
        dispatch_async(queue, ^{
           
            NSLog(@"download----%@",[NSThread currentThread]);
            
            NSURL *url = [NSURL URLWithString:@"http://www.chinanews.com/cr/2014/0108/1576296051.jpg"];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            
            //回到主线程刷新UI
            dispatch_async(dispatch_get_main_queue(), ^{
               NSLog(@"UI----%@",[NSThread currentThread]);
                self.imageView.image = image;
            });
        });
    }

    GCD中常用的函数

    #pragma mark ----------------------
    #pragma mark Events
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        [self apply];
        
    //    MSHPerson *p1 = [[MSHPerson alloc]init];
    //    MSHPerson *p2 = [[MSHPerson alloc]init];
    //    NSLog(@"%@---%@",p1.books,p2.books);
    }
    
    #pragma mark ----------------------
    #pragma Methods
    //延迟执行
    -(void)delay
    {
        NSLog(@"---start---");
        //延迟执行
        //[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
        
        //[self performSelector:@selector(task) withObject:nil afterDelay:3.0];
        
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        //GCD延迟执行
        /*
         第一个参数:表示从什么时候开始计时 DISPATCH_TIME_NOW:现在
         第二个参数:间隔的时间
         第三个参数:队列,决定block在哪个线程中调用,只有当队列是主队列的时候才在主线程调用
         第四个参数:
         */
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
            NSLog(@"----GCD---%@",[NSThread currentThread]);
        });
    }
    
    //栅栏函数
    -(void)barrier
    {
        //1.创建并发队列
        dispatch_queue_t queue = dispatch_queue_create("www.520it", DISPATCH_QUEUE_CONCURRENT);
        
        //2.使用异步函数添加任务
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"download 1--%zd-%@",i,[NSThread currentThread]);
            }
            
        });
        
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"download 2--%zd-%@",i,[NSThread currentThread]);
            }
        });
        
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"download 3--%zd-%@",i,[NSThread currentThread]);
            }
        });
        
        //栅栏函数:控制队列中任务的执行顺序,前面的所有任务执行完毕之后执行栅栏函数,自己执行完毕之后再之后后面的任务
        dispatch_barrier_async(queue, ^{
            NSLog(@"++++++++++++++++++++++++++");
        });
        
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"download 4--%zd-%@",i,[NSThread currentThread]);
            }
        });
        
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"download 5--%zd-%@",i,[NSThread currentThread]);
            }
        });
    }
    
    //一次性代码
    /*保证在整个程序运行过程中执行一次*/
    -(void)once
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"---once---");
        });
    }
    
    //普通遍历和快速迭代比较
    -(void)forAndApply
    {
    //    for (NSInteger i = 0; i<10; i++) {
    //        NSLog(@"%zd----%@",i,[NSThread currentThread]);
    //    }
        
        //1.创建并发队列
        dispatch_queue_t queue = dispatch_queue_create("www.520it", DISPATCH_QUEUE_CONCURRENT);
        
        /*
         第一个参数:遍历的次数
         第二个参数:队列,决定block在哪个线程调用,并发队列
         第三个参数:索引
         */
        dispatch_apply(10, queue, ^(size_t index) {
              NSLog(@"%zd----%@",index,[NSThread currentThread]);
        });
    }
    
    //普通遍历剪切文件
    -(void)moveFile
    {
        //1.拿到文件夹的路径
        NSString *from = @"/Users/xmg/Desktop/from";
        
        //2.拿到目标文件夹的路径
        NSString *to = @"/Users/xmg/Desktop/to";
        
        //3.拿到该文件夹下面所有的文件
        NSArray *subPaths = [[NSFileManager defaultManager] subpathsAtPath:from];
       // NSLog(@"%@",subPaths);
        
        NSInteger count = subPaths.count;
        //4.遍历数组
        for (NSInteger i = 0; i<count; i++) {
            
            //4.0 获得文件的名称
            NSString *fileName = subPaths[i];
            
            //4.1 拼接文件的全路径
            //stringByAppendingPathComponent:在拼接之前添加/
            NSString *fromFullPath = [from stringByAppendingPathComponent:fileName];
            
            //4.2 剪切到什么地方
            NSString *toFullPath = [to stringByAppendingPathComponent:fileName];
            
            NSLog(@"%@---%@---%@",fromFullPath,toFullPath,[NSThread currentThread]);
            //4.3 执行剪切操作
            /*
             第一个参数:文件的路径
             第二个参数:目标路径
             第三个参数:
             */
            NSError *error = nil;
            [[NSFileManager defaultManager] moveItemAtPath:fromFullPath toPath:toFullPath error:&error];
        }
    }
    
    //快速迭代剪切文件
    -(void)apply
    {
        
        //1.拿到文件夹的路径
        NSString *from = @"/Users/xmg/Desktop/from";
        
        //2.拿到目标文件夹的路径
        NSString *to = @"/Users/xmg/Desktop/to";
        
        //3.拿到该文件夹下面所有的文件
        NSArray *subPaths = [[NSFileManager defaultManager] subpathsAtPath:from];
        // NSLog(@"%@",subPaths);
        
        NSInteger count = subPaths.count;
        //4.遍历数组
        dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {
            //4.0 获得文件的名称
            NSString *fileName = subPaths[index];
            
            //4.1 拼接文件的全路径
            //stringByAppendingPathComponent:在拼接之前添加/
            NSString *fromFullPath = [from stringByAppendingPathComponent:fileName];
            
            //4.2 剪切到什么地方
            NSString *toFullPath = [to stringByAppendingPathComponent:fileName];
            
            NSLog(@"%@---%@---%@",fromFullPath,toFullPath,[NSThread currentThread]);
            //4.3 执行剪切操作
            /*
             第一个参数:文件的路径
             第二个参数:目标路径
             第三个参数:
             */
            NSError *error = nil;
            [[NSFileManager defaultManager] moveItemAtPath:fromFullPath toPath:toFullPath error:&error];
        });
    }
    
    //延迟执行的测试方法
    -(void)task
    {
        NSLog(@"---%s",__func__);
    }
    @end
  • 相关阅读:
    【Python】pip导出当前项目所用的包list列表
    什么叫他妈的惊喜
    自我介绍
    IDEA创建SpringBoot时无法连接https://start.spring.io
    漫话docker的衰落与kubernetes的兴起
    基于Kubernetes和OpenKruise的可变基础设施实践
    浅析
    浅析
    浅析
    浅析
  • 原文地址:https://www.cnblogs.com/mshong1616/p/5095745.html
Copyright © 2020-2023  润新知