底层原理

多线程介绍

2021-08-10  本文已影响0人  晨曦的简书

线程和进程

线程和进程的定义

线程:

  • 线程是进程的基本执行单元,一个进程的所有任务都在线程中执行
  • 进程要想执行任务,必须得有线程,进程至少要有一条线程
  • 程序启动会默认开启一条线程,这条线程被称为主线程或 UI 线程

进程:

  • 进程是指在系统中正在运行的一个应用程序
  • 每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存空间内
  • 通过“活动监视器”可以查看 Mac 系统中所开启的进程

进程与线程的关系

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

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

多线程的意义

优点:

  • 能适当提高程序的执行效率
  • 能适当提高资源的利用率(CPU,内存)
  • 线程上的任务执行完成后,线程会自动销毁

缺点

  • 开启线程需要占用一定的内存空间(默认情况下,每一个线程都占 512 KB)
  • 如果开启大量的线程,会占用大量的内存空间,降低程序的性能
  • 线程越多,CPU 在调用线程上的开销就越大
  • 程序设计更加复杂,比如线程间的通信、多线程的数据共享

多线程原理

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

多线程技术方案

C与OC的桥接

线程生命周期

线程池

饱和策略

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

多线程常见问题

  1. 任务执行速度的影响因素有哪些
  1. 优先级翻转

在看优先级反转前先了解什么是IO密集型线程和CPU密集型线程。

IO密集型线程比CPU密集型线程更容易得到优先级提升。
特殊场景下,当多个优先级都比较高的CPU 密集型线程霸占了所有 CPU 资源,而此时优先级较低的 IO 密集型线程将持续等待,产生线程饿死的现象。当然,为了避免线程饿死,CPU会发挥调度作用去逐步提高被“冷落”线程的优先级(提高优先级不一定就会执行,有概率的问题),IO 密集型线程通常情况下比 CPU 密集型线程更容易获取到优先级提升。

  1. 线程优先级影响因素

自旋锁和互斥锁

在我们使用多线程的过程中会遇到一种现象,就是资源抢夺。


例如多窗口售票这个案例,假设现在有 1000 张票,窗口 1 售卖了一张还剩 999 张,但是窗口 2 并不能同步知道剩余票数,所以还是按照 1000 张去售票,这种情况就会出现问题。这个时候我们就需要借助加锁操作来解决这种问题。这里我们介绍两种锁,自旋锁与互斥锁。

互斥锁:

互斥锁参数:

自旋锁:

互斥锁与自旋锁的区别:

atomic 与 nonatomic

atomic:

nonatomic:

下面我们来看一下 atomic 的底层实现:

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    if (offset == 0) {
        object_setClass(self, newValue);
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset);

    if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue);
    }

    // 判断是否是 atomic 标识,是的话就添加锁操作
    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    objc_release(oldValue);
}

在源码中我们可以看到 atomic 其实就是一个标识,底层根据 atomic 标识来判断是否加锁。

上一篇 下一篇

猜你喜欢

热点阅读