美文网首页
2023/03/06 线程

2023/03/06 线程

作者: 大也 | 来源:发表于2023-03-05 15:57 被阅读0次

    进程:正在进行中的程序被称为进程,负责程序运行的内存分配
    每一个进程都有自己独立的虚拟内存空间

    线程:线程是进程中一个独立的执行路径(控制单元)
    一个进程中至少包含一条线程,即主线程

    操作:任务/
    同步操作(sync) :一个接着一个,前一个没有执行完,后面不能执行,不开线程。
    会依次顺序执行,能够决定任务的执行顺序/
    异步操作(async):开启多个新线程,任务同一时间可以一起执行。异步是多线程的代名词
    会并发执行,无法确定任务的执行顺序

    队列:装载线程任务的队形结构。
    (系统以先进先出的方式调度队列中的任务执行)。
    在GCD中有两种队列:串行队列和并发队列。/
    并行队列:队列中的任务通常会并发执行
    线程可以同时一起进行执行。实际上是CPU在多条线程之间快速的切换。(并发功能只有在异步(dispatch_async)函数下才有效)/
    串行队列:队列中的任务只会顺序执行
    线程只能依次有序的执行/
    主队列:主队列负责在主线程上调度任务,如果在主线程上已经有任务正在执行,主队列会等到主线程空闲后再调度任务。通常是返回主线程更新UI的时候使用。dispatch_get_main_queue()/
    全局并发队列:全局并发队列是就是一个并发队列,是为了让我们更方便的使用多线程。dispatch_get_global_queue(0, 0)/

    // 同步执行任务
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            // 任务放在这个block里
            NSLog(@"我是同步执行的任务");
        });
        // 异步执行任务
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // 任务放在这个block里
            NSLog(@"我是异步执行的任务");
        });
    

    使用dispatch_queue_create来创建队列对象,传入两个参数,
    第一个参数表示队列的唯一标识符,可为空。
    第二个参数用来表示串行队列(DISPATCH_QUEUE_SERIAL)或
    并发队列(DISPATCH_QUEUE_CONCURRENT)。

    dispatch_queue_t serialQueue1 = dispatch_queue_create("com.YDW.Queue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("com.YDW.Queue", DISPATCH_QUEUE_CONCURRENT);
    

    主队列:

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
          // 耗时操作放在这里
          dispatch_async(dispatch_get_main_queue(), ^{
              // 回到主线程进行UI操作
          });
      });
    

    全局并发队列:

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //全局并发队列的优先级
    #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高优先级
    #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)优先级
    #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低优先级
    #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台优先级
    //iOS8开始使用服务质量,现在获取全局并发队列时,可以直接传0
    dispatch_get_global_queue(0, 0);
    
    串行同步  
    |123456789|    1个人 1干到9 干完
    串行异步
    |123          |         1个人 1干到3 干完叫
    |     456     |         1个人4干到6  干完叫
    |          789|         1个人7干到9  干完
    并发同步
    |1               |    1个人 1 干完叫
    | 2              |    1个人 2 干完叫
    。。。
    |               9|    1个人 9 干完
    并发异步  一起干
    |1               |    1个人 1 干完
    |2               |    1个人 2 干完
    。。。
    |9               |    1个人 9 干完
    主队列同步
    |123456789|    1个人 1干到9 干完 
    还没干完1就 叫干2  会造成等待死锁
    主队列异步 一起干
    |1               |    1个人 1 干完
    |2               |    1个人 2 干完
    。。。
    |9               |    1个人 9 干完
    
    GCD线程之间的通讯
    开发中需要在主线程上进行UI的相关操作,通常会把一些耗时的操作放在其他线程,比如说图片文件下载等耗时操作。
    
    当完成了耗时操作之后,需要回到主线程进行UI的处理,这里就用到了线程之间的通讯。
    

    当需要等待一会再执行一段代码时,就可以用到这个方法了:dispatch_after。
    和只执行了一次 dispatch_once

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 5秒后异步执行
        NSLog(@"我已经等待了5秒!");
    });
    GCD实现代码只执行一次
    使用dispatch_once能保证某段代码在程序运行过程中只被执行1次。可以用来设计单例。
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"程序运行过程中我只执行了一次!");
    });
    

    GCD栅栏
    当任务需要异步进行,但是这些任务需要分成两组来执行,第一组完成之后才能进行第二组的操作。这时候就用了到GCD的栅栏方法dispatch_barrier_async。

    - (IBAction)barrierGCD:(id)sender {
        // 并发队列
        dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
        // 异步执行
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"栅栏:并发异步1   %@",[NSThread currentThread]);
            }
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"栅栏:并发异步2   %@",[NSThread currentThread]);
            }
        });
        dispatch_barrier_async(queue, ^{
            NSLog(@"------------barrier------------%@", [NSThread currentThread]);
            NSLog(@"******* 并发异步执行,但是34一定在12后面 *********");
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"栅栏:并发异步3   %@",[NSThread currentThread]);
            }
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"栅栏:并发异步4   %@",[NSThread currentThread]);
            }
        });
    }
    

    GCD快速迭代
    GCD有一个快速迭代的方法dispatch_apply,dispatch_apply可以同时遍历多个数字。
    GCD队列组
    队列组有下面几个特点:
    所有的任务会并发的执行(不按序)。
    所有的异步函数都添加到队列中,然后再纳入队列组的监听范围。
    使用dispatch_group_notify函数,来监听上面的任务是否完成,如果完成, 就会调用这个方法。

    - (void)testGroup {
        dispatch_group_t group =  dispatch_group_create();
        dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
            NSLog(@"队列组:有一个耗时操作完成!");
        });
        dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
            NSLog(@"队列组:有一个耗时操作完成!");
        });
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"队列组:前面的耗时操作都完成了,回到主线程进行相关操作");
        });
    }
    

    NSOperation是基于GCD之上的更高一层封装,NSOperation需要配合NSOperationQueue来实现多线程。
    NSOperation实现多线程的步骤如下:

    1. 创建任务:先将需要执行的操作封装到NSOperation对象中。
    2. 创建队列:创建NSOperationQueue。
    3. 将任务加入到队列中:将NSOperation对象添加到NSOperationQueue中
      使用:
      使用子类NSInvocationOperation
      使用子类NSBlockOperation
      定义继承自NSOperation的子类,通过实现内部相应的方法来封装任务。
      NSInvocationOperation
    - (void)testNSInvocationOperation {
        // 创建NSInvocationOperation
        NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
        // 开始执行操作
        [invocationOperation start];
    }
    - (void)invocationOperation {
        NSLog(@"NSInvocationOperation包含的任务,没有加入队列========%@", [NSThread currentThread]);
    }
    

    NSBlockOperation

    - (void)testNSBlockOperationExecution {
        NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"NSBlockOperation运用addExecutionBlock主任务========%@", [NSThread currentThread]);
        }];
        [blockOperation addExecutionBlock:^{
            NSLog(@"NSBlockOperation运用addExecutionBlock方法添加任务1========%@", [NSThread currentThread]);
        }];
        [blockOperation addExecutionBlock:^{
            NSLog(@"NSBlockOperation运用addExecutionBlock方法添加任务2========%@", [NSThread currentThread]);
        }];
        [blockOperation addExecutionBlock:^{
            NSLog(@"NSBlockOperation运用addExecutionBlock方法添加任务3========%@", [NSThread currentThread]);
        }];
        [blockOperation start];
    }
    

    定义继承自NSOperation的子类,通过实现内部相应的方法来封装任务。

    /*******************"WHOperation.h"*************************/
    #import @interface WHOperation : NSOperation
    @end
    /*******************"WHOperation.m"*************************/
    #import "WHOperation.h"
    @implementation WHOperation
    - (void)main {
        for (int i = 0; i < 3; i++) {
            NSLog(@"NSOperation的子类WHOperation======%@",[NSThread currentThread]);
        }
    }
    @end
    /*****************回到主控制器使用WHOperation**********************/
    - (void)testWHOperation {
        WHOperation *operation = [[WHOperation alloc] init];
        [operation start];
    }
    
    //主队列
    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    //非主队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    

    非主队列(其他队列)可以实现串行或并行。
    队列NSOperationQueue有一个参数叫做最大并发数:maxConcurrentOperationCount。
    maxConcurrentOperationCount默认为-1,直接并发执行,所以加入到‘非队列’中的任务默认就是并发,开启多线程。
    当maxConcurrentOperationCount为1时,则表示不开线程,也就是串行。
    当maxConcurrentOperationCount大于1时,进行并发执行。
    系统对最大并发数有一个限制,所以即使程序员把maxConcurrentOperationCount设置的很大,系统也会自动调整。所以把最大并发数设置的很大是没有意义的。
    任务加到队列

    - (void)testOperationQueue {
        // 创建队列,默认并发
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        // 创建操作,NSInvocationOperation
        NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationAddOperation) object:nil];
        // 创建操作,NSBlockOperation
        NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"addOperation把任务添加到队列======%@", [NSThread currentThread]);
            }
        }];
        [queue addOperation:invocationOperation];
        [queue addOperation:blockOperation];
    }
    - (void)invocationOperationAddOperation {
        NSLog(@"invocationOperation===aaddOperation把任务添加到队列====%@", [NSThread currentThread]);
    }
    

    任务加到队列 简便写法

    - (void)testAddOperationWithBlock {
        // 创建队列,默认并发
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 最大并发数为1,串行
        queue.maxConcurrentOperationCount = 1;
        // 最大并发数为2,并发
    //    queue.maxConcurrentOperationCount = 2;
        // 添加操作到队列
        [queue addOperationWithBlock:^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"addOperationWithBlock把任务添加到队列======%@", [NSThread currentThread]);
            }
        }];
    }
    
    - (void)cancelAllOperations;
    - (void)cancel    ;
    // 暂停队列
    [queue setSuspended:YES];
    判断队列是否暂停
    - (BOOL)isSuspended;
    某一个操作(operation2)依赖于另一个操作(operation1),只有当operation1执行完毕,才能执行operation2.
    

    NSOperation的操作依赖

    - (void)testAddDependency {
        // 并发队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        // 操作1
        NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"operation1======%@", [NSThread  currentThread]);
            }
        }];
        // 操作2
        NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"****operation2依赖于operation1,只有当operation1执行完毕,operation2才会执行****");
            for (int i = 0; i < 3; i++) {
                NSLog(@"operation2======%@", [NSThread  currentThread]);
            }
        }];
        // 使操作2依赖于操作1
        [operation2 addDependency:operation1];
        // 把操作加入队列
        [queue addOperation:operation1];
        [queue addOperation:operation2];
    }
    

    https://blog.csdn.net/liqun3yue25/article/details/88258532
    实际运用中还用到的
    iOS GCD group 多请求异步 semaphore 多请求同步
    1.多图上传的功能,而恰巧服务器可能只支持单张上传 重复请求多次接口

    //创建一个组
    dispatch_group_t group = dispatch_group_create();
    for (int i = 0; i <9; I++)
    {//模仿多个网络请求
        dispatch_group_async(group,dispatch_get_global_queue(0, 0), ^{
            //异步网络请求
            
            int x = arc4random() % 5;
            //模拟网络请求快慢不确定的情况
            sleep(x);
            
            NSLog(@"group 请求成功OR请求失败 %d!",i);
        });
    }
    NSLog(@"group开始 网络请求!");
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //网络请求完毕 回到主线程更新UI 或者做些其它的操作
        NSLog(@"group所有请求完毕!!!");
    });
    

    2.用到的 接口请求

    //创建一个组
    dispatch_group_t group = dispatch_group_create();
    for (int i = 0; i <9; I++)
    {//模仿多个网络请求
        
        dispatch_group_enter(group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //异步网络请求
            
            int x = arc4random() % 5;
            //模拟网络请求快慢不确定的情况
            sleep(x);
            NSLog(@"group 请求成功OR请求失败 %d!",i);
            
            dispatch_group_leave(group);
        });
    }
    NSLog(@"group开始 网络请求!");
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //网络请求完毕 回到主线程更新UI 或者做些其它的操作
        NSLog(@"group所有请求完毕!!!");
    });
    

    3.比如要做一个相册功能,也是多图上传,但是要有规律按顺序上传

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        /// 创建一个线程"001" 确保之后不要阻塞主线程
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            /// 创建一个信号量 数值为1  信号量可以让线程阻塞等待
            dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        
            for (int i = 0; i<9; i++) {
                /// 模仿9个请求任务
                /// 执行dispatch_semaphore_wait 信号量数值 -1.
                /// 当i为0此时的信号量数值为0, 当此时的信号量大于等于0继续执行wait函数下面的语句.
                /// 当i为1此时的信号量数值为-1, 阻塞当前线程 阻塞时长为DISPATCH_TIME_FOREVER, 不执行wait函数下面的语句.
                /// 只有等到执行i为0 的 dispatch_semaphore_signal 方法执行, 信号量数值+1 为0, 唤醒 继续执行wait函数下面的语句.
                /// 以此类推循环.
                NSLog(@"当前线程:%@",[NSThread currentThread]);
            
                /// ******这是一个网络请求开始******
                dispatch_async(dispatch_get_global_queue(0, 0), ^{
                    /// 模拟异步网络请求
                    int x = arc4random() % 2;
                    /// 模拟网络请求快慢
                    sleep(x);
                    NSLog(@"执行任务代号:%d",i);
                    /// 任务结束 信号量数值+1 解放线程"001" 阻塞
                    dispatch_semaphore_signal(semaphore);
                });
                /// ******这是一个网络请求结束******
    
                /// 信号量减1 变为负数 当前线程"001" 阻塞
                dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            }
        });
    }
    
    dispatch_group_wait
    暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。
    

    dispatch_source 使用场景:由于 dispatch_source 不依赖于 Runloop,而是直接和底层内核交互,准确性更高,所以经常用于验证码倒计时。

    • (void)use {
      // 倒计时时间
      __block int timeout = 3;
      // 创建队列
      dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);// 创建timerdispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, globalQueue);// 设置1s触发一次,0s的误差/- source 分派源- start 数控制计时器第一次触发的时刻。参数类型是 dispatch_time_t,这是一个opaque类型,我们不能直接操作它。我们得需要 dispatch_time 和 dispatch_walltime 函数来创建它们。另外,常量 DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER 通常很有用。- interval 间隔时间- leeway 计时器触发的精准程度/
      dispatch_source_set_timer(timer,dispatch_walltime(NULL,0),
      1.0*NSEC_PER_SEC, 0);
      // 触发的事件
      dispatch_source_set_event_handler(timer, ^{// 倒计时结束,关闭
      if (timeout <= 0) {// 取消dispatch源
      dispatch_source_cancel(timer);
      }else{
      timeout--;
      dispatch_async(dispatch_get_main_queue(), ^{// 更新主界面的操作
      NSLog(@"倒计时 - %d", timeout);
      });
      }
      });// 开始执行dispatch源
      dispatch_resume(timer);
      }

    https://www.jianshu.com/p/8416d541ef5a
    https://betheme.net/news/txtlist_i192062v.html?action=onClick
    https://www.jianshu.com/p/c1f4d7ef6002

        if (self.cantCommentCourse && self.objectType.intValue == 4) {
            [DJAlertController showTagWithMessage:@"购买后才可发表想法"];
            return;
        }
    
        self.imageUrlString = @"";
        [GBMBProgressHUD showMessage:@""];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            // 执行 同步并且顺序执行
            dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
            __block NSInteger count = 0;
            __block NSMutableArray *imageMArray = imageArrM.mutableCopy;
            for (NSInteger i = 0; i < imageArrM.count; ++i) {
                UIImage *image = [UIImage imageCompressDefaultScale:imageArrM[i]];
                NSData *imageData = UIImageJPEGRepresentation(image, 0.5);
                // 文件命名
                NSDictionary *imageDictionary = @{@"file" : imageData};
    
                dispatch_queue_t concurrentQ = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
                dispatch_async(concurrentQ, ^{
                    @weakify_self
                    [DJDiscoverRecommendManager dj_GEEBOO_file_oss_imgByFile:imageDictionary bizType:@"2" successBlock:^(NSDictionary *data){
                        @strongify_self
                        if ([NSString isEmpty:self.imageUrlString]){
                            self.imageUrlString = data[@"fileName"];
                        }else{
                            self.imageUrlString = [NSString stringWithFormat:@"%@|%@",self.imageUrlString,data[@"fileName"]];
                        }
                        count ++;
                        if (count == imageMArray.count) {
                            dispatch_semaphore_signal(semaphore);
                        }
                    } failureBlock:^(NSError *error){
                        LOG_ME_DEBUG(@"%@",error.domain);
                        count ++;
                        if (count == imageMArray.count) {
                            dispatch_semaphore_signal(semaphore);
                        }else{
                            [GBMBProgressHUD showError:error.domain];
                        }
                    }];
                });
            }
            if (imageMArray.count == 0) {// 没有图片时,终止「阻塞当前线程」
                dispatch_semaphore_signal(semaphore);
            }
    
            // dispatch_semaphore_t 信号量会阻塞当前进程
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            LOG_ME_DEBUG(@"waiting。。。");
    
            NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
            [params setObject:self.objectId forKey:@"objectId"];
            [params setObject:content forKey:@"content"];
            if (self.imageUrlString.length>0) {
               [params setObject:self.imageUrlString forKey:@"images"];
            }
            [params setObject:self.objectType forKey:@"objectType"];
            @weakify_self
            [DJCommentManager dj_commentCommentAddWithParams:params Finished:^(BOOL isHttpSuccess) {
                [GBMBProgressHUD hideHUD];
                @strongify_self
                if (isHttpSuccess) {
                    [self.bottomCommentView initContent];
                }
            }];
        });
    
    
        [self.tableView setHeaderFooterisHidden:YES];
        
        [GBMBProgressHUD showToView:self.view frame:FRAME(0, 0, kScreenWidth, self.view.height)];
        
        dispatch_group_t group = dispatch_group_create();
        dispatch_group_enter(group);
        @weakify_self
        [self getRequestCommendData:^(BOOL isHttpSuccess) {
            @strongify_self
            NSLog(@"评论详情,%@",@(self.tableView.height));
            dispatch_group_leave(group);
        }];
        
        dispatch_group_enter(group);
        self.tableView.sinceId = @"0";
        self.tableView.maxId = @"";
        [self getRequestData:^(BOOL isHttpSuccess) {
            @strongify_self
            NSLog(@"用户回复,%@",@(self.tableView.height));
            dispatch_group_leave(group);
        }];
        
        dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
            dispatch_async(dispatch_get_main_queue(), ^{
                [GBMBProgressHUD hideHUDForView:self.view];
                if (self.commentModel||self.tableView.dataArray.count>0) {
                    [self createRefreshTool];
                    [self setTableHeaderView];
                    self.bottomCommentView.hidden = NO;
                    [self.bottomCommentView revplyParentReplyId:@"0" CommentId:self.commentId NickName:@""];
                }else{
                    [self extractedConfigNoNetView];
                }
            });
        });
    

    相关文章

      网友评论

          本文标题:2023/03/06 线程

          本文链接:https://www.haomeiwen.com/subject/bpbuldtx.html