美文网首页
多线程(理论篇)

多线程(理论篇)

作者: 爪爪123 | 来源:发表于2020-06-28 10:27 被阅读0次

    1.什么是进程,什么是线程?
    进程是一个正在运行的应用程序,一个应用程序可以对应一个或多个进程。应用程序是一个没有生命的实体,只有运行以后,才能称为一个活动的实体,也就是进程。
    进程是操作系统分配资源的基本单元。进程在运行的过程中拥有独立的内存单元,一个进程崩溃后,不会对其他进程造成影响。
    线程是独立运行和独立调度的基本单位,线程才是程序真正的执行单元负责代码的执行。一个进程可以有一个或多个线程,同一个进程的线程共享进程的内存资源。线程没有单独的地址空间,一个线程崩溃整个进程就会崩溃。

    2.什么是多线程?
    多线程的实现原理:事实上,同一时间内单核的CPU只能执行一个线程,多线程是CPU快速的在多个线程之间进行切换(调度),造成了多个线程同时执行的假象。
    如果是多核CPU就真的可以同时处理多个线程了。
    多线程的目的是为了同步完成多项任务,通过提高系统的资源利用率来提高系统的效率。

    3.多线程有什么优点和缺点?
    多线程可以提高系统的资源利用率,从而提高系统的效率。
    开启多线程需要花费时间和空间,开启过多的线程反而会降低性能,cpu频繁的在多个线程中调度会消耗大量的CPU资源,把CPU累死。所以,不要在系统中同时开启过多的子线程。

    4.线程创建实际的开销在内存方面是有多大?
    苹果官方文档说,开启线程需要消耗内存资源和性能。每一个线程都需要分配系统内核空间和程序的内存空间。
    简单来说,创建线程需要花费大约90微秒的时间和512KB的栈空间,以及1KB的内核空间。

    5.多线程在开发中的使用场景?
    在实际开发中应该将一些耗时的操作放在子线程执行,iOS中默认有一个主线程,用来响应用户的手势和刷新UI,如果在主线程执行耗时操作就会把页面卡死,直到执行完了这个操作才能操作界面。一定要在主线程刷新UI的原因:iOS为了保证效率,多线程是线程不安全的,在子线程刷新UI可能会导致未知错误。

    6.iOS中实现多线程的几种方案,各自有什么特点?
    Pthread 是用c语言实现的,非常底层的多线程实现方案,在开发中很少用到。需要程序员手动管理线程的生命周期(手动创建和销毁)。

    NSThread 面向对象的,需要程序员手动创建线程,但不需要手动销毁。子线程间通信很难。
    创建子线程:

    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(testThread:) object:@"我是参数"];
    //有返回值,可以拿到返回值设置线程的优先级和名称
    
    [thread start];//需要手动开启
     
    [NSThread detachNewThreadSelector:@selector(testThread:) toTarget:self withObject:@"便利构造器方式"];//没有返回值,不需要手动开启
     
    [self performSelectorInBackground:@selector(run:)withObject:@"开启后台线程"];//没有返回值,不需要手动开启,只要是NSObject对象或者是NSObject的子类对象都可以用这种方法开启子线程,用下面的方法回到主线程
     
    回到主线程:
    [self performSelectorOnMainThread:@selector(backMainThread) withObject:nil waitUntilDone:YES];//回到主线程的方法
    

    GCDc语言,充分利用了设备的多核,自动管理线程生命周期。比NSOperation效率更高。
    gcd的用法很多,不写了

    NSOperation基于gcd封装,更加面向对象,比gcd多了一些功能。
    两个概念:操作(NSOperation)和队列(NSOperationQueue)
    创建队列:

    NSOperationQueue*mainQueue = [NSOperationQueuemainQueue];//创建主队列,串行队列
     
    NSOperationQueue*queue = [[NSOperationQueue alloc]init];//创建非主队列,同时具备串行队列和并发队列的功能。可以设置最大并发数,实现串行和并行的功能。
    创建任务
     
    NSInvocationOperation* opt1 = [[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(task1)object:nil];
    [queueaddOperation:opt1];//不需要手动启动队列
     
    NSBlockOperation*blockOperation = [NSBlockOperationblockOperationWithBlock:^{NSLog(@"block --- %@",[NSThreadcurrentThread]); }];
    [queue addOperation:blockOperation];
    

    也可以不创建任务,直接用block向队列添加任务:

    [mainQueue addOperationWithBlock:^{           
    self.myImage.image= image;
     NSLog(@"ui-------%@",[NSThreadcurrentThread]);
     }];
    

    原文链接:https://blog.csdn.net/u013983033/article/details/84346836
    ios多线程开发的常用四种方式 (附有demo)

    1. pthread
    2. NSThread
    3. NSOperation\NSOperationQueue
    4. GCD

    pthread
    C语言通用的多线程API,跨平台,程序员手动管理线程生命周期,使用难度大
    代码实现

     //创建线程
     NSLog(@"开始执行");
     int pthread_create(pthread_t * __restrict ,const pthread_attr_t * __restrict ,void *(*)(void *),void * __restrict);
     //使用
     pthread_t pthread;
     pthread_create(&pthread, NULL, function, NULL);
     NSLog(@"执行结束");
    
    void * function(void * param) {
        for (int i = 0; i < 5; i ++) {
            NSLog(@"i : %d-- 线程: %@",i,[NSThread currentThread]);
        }
        return NULL;
    }
    

    运行结果

    2018-11-22 11:49:57.045504+0800 Multithreading[20231:4407861] 开始执行
    2018-11-22 11:49:57.045716+0800 Multithreading[20231:4407861] 执行结束
    2018-11-22 11:49:57.045828+0800 Multithreading[20231:4409725] i : 0–
    线程: <NSThread: 0x6080002724c0>{number = 3, name = (null)} 2018-11-22
    11:49:57.046230+0800 Multithreading[20231:4409725] i : 1-- 线程:
    <NSThread: 0x6080002724c0>{number = 3, name = (null)} 2018-11-22
    11:49:57.047038+0800 Multithreading[20231:4409725] i : 2-- 线程:
    <NSThread: 0x6080002724c0>{number = 3, name = (null)} 2018-11-22
    11:49:57.047152+0800 Multithreading[20231:4409725] i : 3-- 线程:
    <NSThread: 0x6080002724c0>{number = 3, name = (null)} 2018-11-22
    11:49:57.047439+0800 Multithreading[20231:4409725] i : 4-- 线程:
    <NSThread: 0x6080002724c0>{number = 3, name = (null)}
    
    

    二、 NSThread
    创建NSThread 有三种方法

    //公共方法
    - (void)threadMoth {
        NSLog(@"%@",[NSThread currentThread]);
    }
    
    // 方法 1 需要手动开启
    NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
    [thread start];
    //方法 2
    [NSThread detachNewThreadSelector:@selector(threadMoth) toTarget:self withObject:nil];
    //方法 3
    [self performSelectorInBackground:@selector(threadMoth) withObject:nil];
    

    常用的相关方法:

    [NSThread mainThread];// 获得主线程
    [NSThread isMainThread]; //是否为主线程
    NSLog(@"shuchu--%@--%d",[NSThread mainThread],[NSThread isMainThread]);
    NSLog(@"休眠前");
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4.0]];//阻塞线程 以当前时间为准阻塞4秒
    NSLog(@"休眠1");
    [NSThread sleepForTimeInterval:2];//阻塞线程 2 秒
    NSLog(@"休眠后");
    [NSThread exit];// 强制退出线程
    

    方法2、一调用就会立即创建一个线程来做事情;
    方法1、需要手动调用 start 启动线程时才会真正去创建线程。
    方法3、是利用NSObject的方法 performSelectorInBackground:withObject: 来创建一个线程:在后台运行某一个方法
    与 NSThread 的 detachNewThreadSelector:toTarget:withObject: 是一样的。
    运行结果

    2018-11-22 16:17:36.959316+0800 Multithreading[20834:4901061]
    <NSThread: 0x60c00007b800>{number = 4, name = (null)} 2018-11-22
    16:17:36.959318+0800 Multithreading[20834:4901060] <NSThread:
    0x60c00007b8c0>{number = 3, name = (null)} 2018-11-22
    16:17:36.960902+0800 Multithreading[20834:4901062] <NSThread:
    0x60c00007bc40>{number = 5, name = (null)}
    

    NSOperation、NSOperationQueue
    NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。

    (1)、NSOperation
    NSOperation 是个抽象类,不能用来封装操作。我们只有使用它的子类来封装操作。我们有两种方式来封装操作。

    子类 NSInvocationOperation
    子类 NSBlockOperation
    1、 子类 NSInvocationOperation

    创建 NSInvocationOperation 对象

    NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
    

    调用 start 方法开始执行操作

    [operation start];
    

    运行结果

    2018-11-22 16:17:36.959446+0800 Multithreading[20834:4899673]
    <NSThread: 0x600000075840>{number = 1, name = main}
    

    通过运行结果 可以看到 在没有加入到NSOperationQueue中,而是单独使用的时候,NSInvocationOperation
    并没有开启新的线程,还是在主线程中执行的

    2、子类NSBlockOperation

    创建对象

    NSBlockOperation * block = [NSBlockOperation blockOperationWithBlock:^{
         NSLog(@"block-%@",[NSThread currentThread]);
    }];
    
    

    添加额外的操作

    [block addExecutionBlock:^{
         NSLog(@"block-%@",[NSThread currentThread]);// 打印当前线程
    }];
    
    

    调用 start 方法开始执行操作

    [block start];
    
    

    运行结果

    2018-11-22 16:41:36.057252+0800 Multithreading[20893:4948069]
    <NSThread: 0x600000074a00>{number = 1, name = main}
    

    和NSInvocationOperation 使用一样。因为代码是在主线程中调用的,所以打印结果为主线程。如果在其他线程中执行操作,则打印结果为其他线程。 通过 addExecutionBlock: 就可以为 NSBlockOperation 添加额外的操作。
    (2)、NSOperationQueue

    NSOperation 需要配合 NSOperationQueue 来实现多线程, 需要将创建好的操作加入到队列中去。总共有两种方法:

    第一种方法:

    - (void)addOperation:(NSOperation *)op;
    
    

    需要先创建操作,再将创建好的操作加入到创建好的队列中去。代码实现

    //创建队列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    //添加操作
    NSInvocationOperation * operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
    NSInvocationOperation * operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
    NSBlockOperation * block1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block1-%@",[NSThread currentThread]);// 打印当前线程
    }];
    [block1 addExecutionBlock:^{
        NSLog(@"block2-%@",[NSThread currentThread]);// 打印当前线程
    }];
    //3 添加队列
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:block1];
    
    

    运行结果

    2018-11-22 17:27:58.671071+0800 Multithreading[23002:5205701]
    <NSThread: 0x600000262ac0>{number = 3, name = (null)}
    2018-11-22 17:27:58.671073+0800 Multithreading[23002:5205666] queue1-<NSThread: 0x60c000265a80>{number = 5, name = (null)}
    2018-11-22 17:27:58.671071+0800 Multithreading[23002:5205664] block1-<NSThread: 0x608000260400>{number = 6, name = (null)}
    2018-11-22 17:27:58.671115+0800 Multithreading[23002:5205663] <NSThread: 0x608000260340>{number = 4, name = (null)}
    

    使用 NSOperation 子类创建操作,并使用 addOperation: 将操作加入到操作队列后能够开启新线程,进行并发执行。

    第二种方法:

    - (void)addOperationWithBlock:(void (^)(void))block;
    
    

    无需创建操作,在 block 中添加操作,直接将包含操作的 block 加入到队列中。

    NSOperationQueue * queue1 = [[NSOperationQueue alloc] init];
    [queue1 addOperationWithBlock:^{
        NSLog(@"queue1-%@",[NSThread currentThread]);// 打印当前线程
    }];
    [queue1 addOperationWithBlock:^{
        NSLog(@"queue1-%@",[NSThread currentThread]);// 打印当前线程
    }];
    [queue1 addOperationWithBlock:^{
        NSLog(@"queue1-%@",[NSThread currentThread]);// 打印当前线程
    }];
    [queue1 addOperationWithBlock:^{
        NSLog(@"queue1-%@",[NSThread currentThread]);// 打印当前线程
    }];
    
    

    运行结果

    2018-11-22 17:27:58.671156+0800 Multithreading[23002:5205665]
    queue1-<NSThread: 0x6000002613c0>{number = 7, name = (null)}
    2018-11-22 17:27:58.671322+0800 Multithreading[23002:5205664] block2-<NSThread: 0x608000260400>{number = 6, name = (null)}
    2018-11-22 17:27:58.671331+0800 Multithreading[23002:5205666] queue1-<NSThread: 0x60c000265a80>{number = 5, name = (null)}
    2018-11-22 17:27:58.671367+0800 Multithreading[23002:5205701] queue1-<NSThread: 0x600000262ac0>{number = 3, name = (null)}
    

    使用 addOperationWithBlock: 将操作加入到操作队列后能够开启新线程,进行并发执行。

    四、 GCD

    GCD会自动利用更多的CPU内核,会自动管理线程的生命周期,不需要写管理线程的代码。定制任务 将任务添加到队列中 GCD会自动将队列中的任务取出,放到线程中去执行,任务的取出遵循FIFO原则。

    Main queue:
      运行在主线程,由dispatch_get_main_queue获得.和ui相关的就要使用MainQueue,在主线程中更新ui.
    Serial quque(private dispatch queue)
      每次运行一个任务,可以添加多个,执行次序FIFO.
    Concurrent queue(globaldispatch queue):
    可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务.使用dispatch_get_global_queue获得.
     同步 在当前线程中执行
     dispatch_sync(dispatch_queue_t queue, ^(void)block)
     异步 可以在新的线程中执行,有开新线程的能力(不是一定会开新线程,比如放在主队列中)
     dispatch_async(dispatch_queue_t queue, ^(void)block)
    

    常用地方

    //延迟执行
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延时2秒执行");
    });
    
    //全局异步并发队列
    /**
     参数 identifier 优先级
     参数 flags      保留参数 默认传0
     */
    dispatch_queue_t queuet = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queuet, ^{
        NSLog(@"queuet --- 1");
    });
    dispatch_async(queuet, ^{
        NSLog(@"queuet --- 2");
    });
    dispatch_async(queuet, ^{
        NSLog(@"queuet --- 3");
    });
    
    //自定义并发队列
    dispatch_queue_t queuet1 = dispatch_queue_create("www.baidu.com", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queuet1, ^{
        NSLog(@"queuet1 --- 1");
    });
    dispatch_async(queuet1, ^{
        NSLog(@"queuet1 --- 2");
    });
    dispatch_async(queuet1, ^{
        NSLog(@"queuet1 --- 3");
    });
    
    dispatch_queue_t queuet2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queuet2, ^{
        NSLog(@"queuet2 --- 1");
    });
    dispatch_barrier_async(queuet2, ^{
        NSLog(@"queuet2 --- 2");
    });
    
    //group
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"group1");
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"group2");
    });
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"group3");
    });
    
    
    //创建单例
    static dispatch_once_t onceToken;
    static ViewController * viewController;
    dispatch_once(&onceToken, ^{
        viewController = [[ViewController alloc] init];
    });
    return viewController;
    
    

    五、线程安全问题
    资源共享:
    一块资源可能会被多个线程共享,比如多个线程访问同一个对象、对一个变量、同一个文件

     解决方法:
     互斥锁
     @synchronized(锁对象){ //需要锁定的代码 }
     或者 加NSLock锁
     
     原子和非原子属性
     atomic 默认 原子属性 为setter方法加锁
     线程安全,需要消耗大量的资源
     nonatomic 非原子属性 不会为setter方法加锁
     非线程安全,适合内存小的移动设备
     
     注意:
     所有属性都声明为nonatomic
     尽量避免多线程抢夺同一块资源
     尽量加加锁、资源抢夺的业务逻辑交给服务器处理,减小移动客户端压力
    
    

    相关文章

      网友评论

          本文标题:多线程(理论篇)

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