美文网首页
多线程(一)

多线程(一)

作者: 镜像 | 来源:发表于2021-08-10 14:57 被阅读0次

    线程与进程

    • 线程是进程的基本执行单元,一个进程所有任务都在线程中执行

    • 进程想要执行任务,必须得有线程,进程至少要有一条线程

    • 程序启动会默认开启一条线程,这条线程被称为主线程或UI线程

    • 进程实质在系统中正在运行的一个应用程序

    • 每个进程之前是独立的,每个进程均运行在其专用的且受保护的内存空间内

    • 通过“活动监视器”可以查看Mac系统中所开启的进程

    进程与线程的关系

    地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间
    资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之前的资源是独立的。

    1. 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
    2. 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程。
    3. 执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须已存在应用程序中,由应用程序提供多个线程执行控制。
    4. 线程是处理器调度的基本单位,但进程不是。
    5. 线程没有地址空间,线程包含在进程地址空间中。

    时间片的概念:CPU在多个任务直接进行快速的切换,这个时间间隔就是时间片

    • 单核CPU同一时间,只能处理一个线程
    • 多线程同时执行其实是CPU快速的在多个线程之前切换,CPU调度线程的时间足够快,就造成了多线程的“同时”执行的效果
    • 如果线程数非常多,CPU会在n个线程之间切换,消耗大量的CPU资源,导致每个线程呗调度的次数会降低,线程的执行效率降低
    线程生命周期 可调度线程池

    饱和策略:

    • AbortPolicy 直接抛出RejectedExecutionExeception异常来阻止系统正常运行
    • CallerRunsPolicy 将任务退回到调度者
    • DisOldestPolicy 丢掉等待最久的任务
    • DisCardPolicy 直接丢弃任务

    这四种拒绝策略均实现在RejectedExecutionHandler接口

    任务执行速度的影响因素

    1. cpu状态
    2. 任务复杂度
    3. 优先级
    4. 线程状态

    优先级反转:

    1. IO 密集型,频繁等待
    2. CPU 密集型,很少等待
      IO比CPU更容易得到优先级提升

    优先级影响因素:

    1. 用户指定
    2. 等待的频繁度,频繁调用优先级会降低
    3. 长时间不执行,会提升优先级

    互斥锁 发现其他线程执行 当前线程 休眠 (就绪状态) 一直在等打开 唤醒执行
    自旋锁 发现其他线程执行 当前线程 询问 - 忙等 耗费性能比较高

    在短小精悍的项目里用自旋锁,或者环境资源充足,比如mac环境,用自旋锁

    atomic 是原子属性,是为多线程开发准备的,是默认属性!
    仅仅在属性的 setter 方法中,增加了锁(自旋锁),能够保证同一时间,只有一条线程对属性进行写操作
    同一时间 单(线程)写多(线程)读的线程处理技术
    nonatomic 是非原子属性
    没有锁!性能高!

    在源码中可以看到

    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }
    

    下面看个面试题:

    dispatch_queue_t queue = dispatch_queue_create("sj", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
    

    输出结果顺序是:1 5 2 3 4

    image

    再看下面一段代码:

    dispatch_queue_t queue = dispatch_queue_create("sj", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
    

    输出结果是: 1 5 2 崩溃

    image

    这个会造成死锁。第二个块会往第一个块后面增加一个任务,第二个块任务不执行完,是执行不了第一个块下面的代码,但是第一个块代码不执行完,又不能执行第二个块里面的代码,相互等待造成死锁。

    再看一种情况:

    dispatch_queue_t queue = dispatch_queue_create("sj", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_async(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
    

    输出结果是: 1 5 2 4 3

    image

    相关文章

      网友评论

          本文标题:多线程(一)

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