C++面试

目录

1.进程线程的区别

2.多线程和多进程的各自优势

3.如何理解信号量,应用场景

4.死锁的产生,四个必要条件,死锁的避免和预防

5.用数组实现栈


1.进程线程的区别

进程和线程都是计算机中的执行单元,但它们有以下的区别:

  1. 资源分配:每个进程都有独立的内存空间和系统资源,如文件描述符、环境变量等,而线程则共享所属进程的资源。

  2. 开销:创建一个进程的开销比创建一个线程的开销大,因为进程需要独立的虚拟地址空间、内存空间和系统资源。线程则只需要创建调度线程所需的堆栈和程序计数器即可,内存和系统资源的开销较小。

  3. 执行方式:进程之间相互独立,互不干扰,每个进程都有自己独立的处理器调度,而线程则是在同一个进程内执行,使用同一个进程的处理器调度。

  4. 通讯方式:进程之间需要使用 IPC (Interprocess Communication) 机制实现进程之间的通讯,如管道、消息队列等;而线程则可以通过共享变量等方式直接进行通讯。

总体来说,当需要进行并行计算,或者需要独立的地址空间和资源分配时,应选择进程;而需要多个执行流在同一个进程空间内协作完成任务时,应选择线程。

2.多线程和多进程的各自优势

多线程和多进程都是实现并发性的技术,但它们各有优势:

多线程:

  1. 轻量级:线程创建和销毁需要的时间和资源都比进程少。
  2. 共享进程资源:多个线程可以共享同一个进程中的资源,如内存、文件等,便于数据共享和通信。
  3. 运行速度快:由于线程间切换的开销较小,所以多线程通常比多进程更高效。
  4. 适用于IO密集型任务:多线程可以轻松地在IO密集型任务中实现并发操作。

多进程:

  1. 更安全:多进程间不会相互干扰,若某个进程崩溃了,其他进程不会受到任何影响。
  2. 更稳定:多进程可以分配到多个物理核心,可以充分利用机器的CPU,提高并发处理能力。
  3. 更适用于CPU密集型任务:多进程可以充分使用多核CPU,更适用于CPU密集型任务。
  4. 可以利用分布式系统:多进程可以利用分布式系统的优势,分配到多个计算机上,实现更高的并发量。

3.如何理解信号量,应用场景

信号量是一个用来控制并发访问的计数器,它可以通过互斥操作的方式来确保同一时刻只有一个进程可以访问共享资源。通常情况下,一个信号量提供两个操作:wait(等待)和signal(通知)。wait操作会检查信号量的值,如果其值大于0,则将其减1,表示此时共享资源被一个进程占用了;如果其值等于0,则进程将被阻塞等待其他进程释放资源。signal操作则会将信号量的值加1,表示当前进程已经释放了共享资源,可以让其他进程继续使用。

应用场景包括:

  1. 控制并发访问共享资源:比如多进程同时访问一个文件,需要使用信号量对文件资源进行控制,以避免竞争导致数据不一致。

  2. 控制进程执行顺序:比如在一个多进程并行的程序中,有些进程需要等待其他进程执行完毕才能开始执行,这时可以使用信号量来控制进程执行的顺序。

  3. 控制线程同步:在多线程编程中,需要使用信号量来控制线程的同步,以避免同时访问共享资源导致的数据混乱。

死锁是在多道程序环境下可能会出现的一种状态,其中进程之间互相等待,无法继续执行下去。死锁的产生必须具备四个必要条件:

4.死锁的产生,四个必要条件,死锁的避免和预防

  1. 互斥条件:进程所请求的资源不能同时被其他进程所共享,只能由一个进程占用;
  2. 请求和保持条件:一个进程在请求资源时,同时保持对已获取到的资源的占用;
  3. 不剥夺条件:资源只能由占有它的进程自愿释放,其他进程不能剥夺;
  4. 循环等待条件:多个进程之间形成一种头尾相接的循环等待资源的关系。

解决死锁的方法包括:

  1. 避免死锁:在分配资源时,根据进程对资源的请求及使用顺序进行合理调度。避免死锁的方法比较保守,容易引起系统资源的浪费。
  2. 预防死锁:采用一定的策略,对可能导致死锁的条件进行预判,在进入死锁状态之前预防其出现。
  3. 检测死锁:对系统的资源分配状态进行监视,发现死锁后进行解除。
  4. 解除死锁:当发生死锁时,可以通过抢占资源、剥夺资源、回退和重新分配等方法解除死锁状态。但这种方法成本较高,同时也有一定的风险。

5.用数组实现栈

#include 
using namespace std;

class Stack {
  private:
    int *items; // 栈的数组
    int top; // 栈顶元素的索引
    int size; // 栈的容量

  public:
    // 构造函数
    Stack(int size) {
      this->size = size;
      this->items = new int[size];
      this->top = -1; // 空栈的标志
    }

    // push方法:向栈顶添加元素
    void push(int element) {
      if (top == size - 1) { // 栈满
        cout << "Stack overflow!" << endl;
      } else {
        top++;
        items[top] = element;
      }
    }

    // pop方法:从栈顶弹出元素
    int pop() {
      if (top == -1) { // 空栈
        cout << "Stack underflow!" << endl;
        return -1;
      } else {
        int element = items[top];
        top--;
        return element;
      }
    }

    // isEmpty方法:判断栈是否为空
    bool isEmpty() {
      return (top == -1);
    }

    // isFull方法:判断栈是否满
    bool isFull() {
      return (top == size - 1);
    }
};

你可能感兴趣的:(面经,面试,java,职场和发展,c++,leetcode)