首页 > 系统相关 >线程进程以及多线程多进程(超详解)

线程进程以及多线程多进程(超详解)

时间:2024-06-24 10:02:55浏览次数:3  
标签:共享内存 int 线程 进程 shared 多线程

目录

前言

一、什么是进程和线程

进程 (Process)

线程 (Thread)

多线程 (Multithreading)

多进程 (Multiprocessing)

相互组合关系

二、资源分配

进程

私有资源

共享资源

线程

私有资源

共享资源

多进程

私有资源

共享资源

多线程

私有资源

共享资源

进程的共享和私有资源

线程的共享和私有资源

三、多进程和多线程间访问资源的保护

多进程中的资源保护

多线程中的资源保护

多线程示例

多进程示例

四、通信方式

多进程通信方式

多线程通信方式

五、总结

1.定义和概念

2. 内存和资源管理

3. 切换和调度

4. 并发性和效率

5. 编程模型和适用场景



前言

多线程和多进程是实现并发执行的两种常见方式,它们在操作系统和编程领域中被广泛使用。

一、什么是进程和线程

进程 (Process)

  • 定义:进程是程序执行时的一个实例。在计算机中,每个进程都有自己独立的内存空间,包括代码、数据和堆栈等,同时拥有一组系统资源,如文件描述符、信号、处理器状态等。
  • 特点:
    • 独立性:每个进程在执行过程中都是独立的,一个进程的崩溃不会影响其他进程。
    • 资源分配:操作系统为每个进程分配独立的内存空间和资源,进程间相互隔离。
    • 可调度性:操作系统可以对进程进行调度,分配处理器时间片,以实现多任务并发执行。

线程 (Thread)

  • 定义:线程是进程内的一个执行单元,是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程,它们共享进程的资源,如内存、文件等。
  • 特点:
    • 共享资源:同一进程内的线程共享进程的地址空间和系统资源,可以直接访问共享数据。
    • 轻量级:相对于进程来说,线程的创建和切换开销较小,可以更高效地实现并发。
    • 并发性:多个线程可以在同一进程内并发执行,提高程序的响应速度和资源利用率。

多线程 (Multithreading)

  • 定义:多线程是指在单个进程内同时执行多个线程的机制。一个进程可以包含多个线程,每个线程独立执行不同的任务,共享进程的资源。
  • 特点:
    • 轻量级:线程是进程内的一个执行单元,相对于进程来说创建和切换开销较小。
    • 共享资源:同一进程内的线程共享地址空间、文件描述符等进程资源,可以直接访问共享数据。
    • 通信简便:线程之间的通信和同步相对较为简单,可以使用共享内存、信号量、互斥锁等机制。

多进程 (Multiprocessing)

  • 定义:多进程是指在操作系统中同时执行多个独立的进程,每个进程有自己独立的地址空间和资源。
  • 特点:
    • 独立性:每个进程拥有独立的内存空间和资源,一个进程的崩溃不会影响其他进程。
    • 进程间通信:进程间通信需要借助操作系统提供的机制,如管道、消息队列、共享内存等。
    • 开销较大:与线程相比,创建和切换进程的开销较大,因为进程需要分配独立的内存空间。

相互组合关系

  • 单进程单线程:一个进程中只有一个线程,所有任务由这个单线程执行。
+-------------------+
|    进程 A         |
|  +-------------+  |
|  |   线程 A1   |  |  <- 单线程
|  +-------------+  |
+-------------------+
  • 单进程多线程:一个进程中包含多个线程,这些线程可以并发执行,但共享进程的资源。
+-------------------+
|    进程 B         |
|  +-------------+  |
|  |   线程 B1   |  |  
|  +-------------+  |
|  +-------------+  |
|  |   线程 B2   |  |  
|  +-------------+  |
|  +-------------+  |
|  |   线程 B3   |  |  <- 多线程
|  +-------------+  |
+-------------------+
  • 多进程单线程:每个进程都有自己独立的资源和内存空间,每个进程中只有一个线程。
+-------------------+
|    进程 C         |
|  +-------------+  |
|  |   线程 C1   |  |  <- 单线程
|  +-------------+  |
+-------------------+

+-------------------+
|    进程 D         |
|  +-------------+  |
|  |   线程 D1   |  |  <- 单线程
|  +-------------+  |
+-------------------+
  • 多进程多线程:每个进程都有自己独立的资源和内存空间,每个进程内部包含多个线程,这些线程可以并发执行并共享进程资源。
+-------------------+
|    进程 E         |
|  +-------------+  |
|  |   线程 E1   |  |  
|  +-------------+  |
|  +-------------+  |
|  |   线程 E2   |  |  <- 多线程
|  +-------------+  |
+-------------------+

+-------------------+
|    进程 F         |
|  +-------------+  |
|  |   线程 F1   |  |  
|  +-------------+  |
|  +-------------+  |
|  |   线程 F2   |  |  <- 多线程
|  +-------------+  |
|  +-------------+  |
|  |   线程 F3   |  |  
|  +-------------+  |
+-------------------+

二、资源分配

进程

私有资源
  • 地址空间:每个进程都有自己独立的地址空间,这包括代码段、数据段、堆和堆栈。
  • 文件描述符表:每个进程有自己的文件描述符表,用于管理打开的文件和I/O设备。
  • 信号处理:每个进程有自己的信号处理机制,独立于其他进程。
  • 安全属性:每个进程有自己的用户ID和权限信息。
共享资源
  • 共享内存:尽管进程之间通常是独立的,但可以通过共享内存区段进行通信。
  • 信号量:用于进程间同步,可以在多个进程之间共享。
  • 消息队列:一种进程间通信机制,允许进程间发送和接收消息。

线程

私有资源
  • :每个线程有自己的栈,用于存储局部变量、函数调用信息等。
  • 寄存器:每个线程有自己独立的寄存器状态,包括程序计数器(PC)。
  • 线程控制块(TCB):每个线程有自己的控制信息,如线程ID、状态、优先级等。
共享资源
  • 地址空间:同一进程内的所有线程共享相同的地址空间,包括代码段、数据段和堆。
  • 全局变量:所有线程可以访问同一进程的全局变量。
  • 文件描述符表:每个线程可以访问和操作同一进程的文件描述符表。
  • 动态内存:线程共享同一进程的堆区,可以通过动态内存分配(malloc/free)进行共享数据的读写。

多进程

私有资源
  • 独立的地址空间:每个进程有各自独立的地址空间和所有相关资源。
  • 文件描述符表:每个进程维护自己的文件描述符表。
共享资源
  • 共享内存:多进程可以通过共享内存区块进行通信。
  • IPC机制:包括管道、消息队列、信号量等。

多线程

私有资源
  • 线程栈:每个线程有自己的栈。
  • 线程寄存器:每个线程有自己的寄存器状态。
共享资源
  • 进程地址空间:所有线程共享进程的整个地址空间,包括代码段、数据段和堆。
  • 文件描述符表:所有线程共享同一进程的文件描述符表。
  • 全局变量:所有线程可以访问和修改进程的全局变量。

进程的共享和私有资源

// 进程 A
int main() {
    int a = 10; // 局部变量(私有)
    int *shared_mem = (int *)shmget(key, size, IPC_CREAT | 0666); // 共享内存(共享)
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程
        shared_mem[0] = 20; // 修改共享内存中的值
        exit(0);
    } else {
        wait(NULL); // 等待子进程结束
        printf("Shared memory value: %d\n", shared_mem[0]); // 应该输出20
    }
    return 0;
}

线程的共享和私有资源

#include <pthread.h>
#include <stdio.h>

int global_var = 0; // 全局变量(共享)

void *thread_func(void *arg) {
    int local_var = 5; // 局部变量(私有)
    global_var++; // 修改全局变量
    printf("Local variable: %d\n", local_var);
    printf("Global variable: %d\n", global_var);
    return NULL;
}

int main() {
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, thread_func, NULL);
    pthread_create(&tid2, NULL, thread_func, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    printf("Final global variable: %d\n", global_var); // 应该输出2
    return 0;
}

三、多进程和多线程间访问资源的保护

多进程中的资源保护

  1. 使用进程间通信(IPC)机制

    • 信号量(Semaphore):通过信号量实现进程对共享资源的互斥访问控制,确保同一时刻只有一个进程可以访问共享资源。
    • 互斥锁:在共享内存中使用互斥锁来保护临界区域,只有成功获取锁的进程才能进入临界区域执行操作。
    • 条件变量:结合互斥锁使用,实现进程间的同步,确保在特定条件下才能访问或修改共享资源。
  2. 使用文件锁

    如果共享资源是文件或文件区域,可以使用文件锁(如 fcntl() 函数提供的锁机制)来确保同一时刻只有一个进程可以对文件进行读写操作。
  3. 共享内存管理

    通过分配和释放共享内存的方式,确保只有一个进程可以修改内存区域,或者使用特定的同步机制来协调多个进程对共享内存的访问。

多线程中的资源保护

  1. 互斥锁(Mutex)

    最常用的方法是使用互斥锁来保护临界区域,确保同一时刻只有一个线程可以进入临界区域执行操作。线程在进入临界区前必须先获取锁,并在退出时释放锁。
  2. 条件变量

    用于在线程间进行通信和同步,例如等待某个条件为真时才继续执行,可以防止竞态条件的发生。
  3. 读写锁(Read-Write Lock)

    如果多个线程需要读取共享资源,但很少修改它,可以使用读写锁来允许多个线程同时读取,但只有一个线程可以写入。
  4. 原子操作

    使用原子操作可以保证某些简单操作的原子性,如递增操作等,这样可以避免竞态条件的发生。

多线程示例

在多线程场景下,可以使用标准库提供的 std::mutexstd::thread 来实现资源的保护。确保多个线程不会同时修改这个共享变量

#include <iostream>
#include <thread>
#include <mutex>

const int NUM_THREADS = 2;
const int MAX_COUNT = 100000;

struct SharedData {
    int shared_resource;
    std::mutex mtx;
};

void worker(SharedData& data) {
    for (int i = 0; i < MAX_COUNT; ++i) {
        // 每个线程在执行 worker 函数时,会自动对 data.mtx 进行加锁和解锁,从而保证线程安全。
        std::lock_guard<std::mutex> lock(data.mtx); // 使用互斥量保护临界区域
        data.shared_resource++;
    }
}

int main() {
    SharedData shared_data;
    shared_data.shared_resource = 0;

    std::thread threads[NUM_THREADS];
    for (int i = 0; i < NUM_THREADS; ++i) {
        threads[i] = std::thread(worker, std::ref(shared_data));
    }

    for (int i = 0; i < NUM_THREADS; ++i) {
        threads[i].join();
    }

    std::cout << "Final value of shared_resource: " << shared_data.shared_resource << std::endl;

    return 0;
}

多进程示例

在Linux下,可以使用命名信号量(named semaphore)来实现多进程间的资源保护。

#include <iostream>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>

const char* SEM_NAME = "/my_semaphore";
const int NUM_PROCESSES = 2;
const int MAX_COUNT = 100000;

struct SharedMemory {
    int shared_resource;
    sem_t mutex;
};

int main() {
    // 使用 shm_open 和 mmap 创建共享内存,使得多个进程可以访问同一块内存区域。
    int shm_fd = shm_open("/my_shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, sizeof(SharedMemory));
    SharedMemory* shared_memory = (SharedMemory*)mmap(NULL, sizeof(SharedMemory), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    //初始化一个命名信号量 mutex,用于保护 shared_resource 的访问。
    sem_init(&(shared_memory->mutex), 1, 1); // 初始化互斥信号量

    pid_t pid;
    for (int i = 0; i < NUM_PROCESSES; ++i) {
        pid = fork();
        if (pid == 0) { // 子进程
            for (int j = 0; j < MAX_COUNT; ++j) {
                // 每个子进程通过 sem_wait 和 sem_post 来对 mutex 进行加锁和解锁,
                // 确保每次只有一个进程可以访问共享资源。
                sem_wait(&(shared_memory->mutex)); // 等待信号量
                shared_memory->shared_resource++;
                sem_post(&(shared_memory->mutex)); // 发送信号量
            }
            return 0;
        }
    }

    // 等待所有子进程结束
    for (int i = 0; i < NUM_PROCESSES; ++i) {
        wait(NULL);
    }

    std::cout << "Final value of shared_resource: " << shared_memory->shared_resource << std::endl;

    // 清理资源
    munmap(shared_memory, sizeof(SharedMemory));
    shm_unlink("/my_shared_memory");
    sem_destroy(&(shared_memory->mutex));

    return 0;
}

四、通信方式

多进程通信方式

多进程间通信(Inter-Process Communication, IPC)通常涉及在不同的进程之间传递数据或同步操作。以下是几种常见的多进程通信方式:

  1. 管道(Pipe)

    管道是Unix/Linux系统中最简单的IPC形式之一,它可以在两个相关的进程之间传递数据。管道通常是单向的,即只能用于单向数据流,但可以通过创建两个管道实现双向通信。
  2. 命名管道(Named Pipe)

    类似于管道,但可以通过文件系统进行命名和访问,允许无亲缘关系的进程之间进行通信。
  3. 消息队列(Message Queue)

    消息队列是一种通过消息传递进行通信的机制,允许进程通过向队列发送和接收消息来进行通信。
  4. 共享内存(Shared Memory)

    共享内存是进程间通信的高效方式,它允许多个进程访问同一个逻辑内存区域,实现快速的数据共享。(速度快,但要同步)
  5. 信号量(Semaphore)

    信号量是一种计数器,用于多进程间的同步,可以用来解决多个进程竞争有限资源的问题。
  6. 套接字(Socket)

    套接字不仅用于网络编程,也可以在同一台机器上的不同进程间进行通信,提供了灵活和强大的通信能力。

多线程通信方式

多线程通信通常发生在同一进程内的不同线程之间,以下是常见的多线程通信方式:

  1. 共享内存

    在多线程中,共享内存仍然是一种有效的通信方式,因为所有线程都可以直接访问共享的内存数据结构。
  2. 互斥锁(Mutex)

    互斥锁是最基本的线程同步机制,它可以保护临界区,确保同时只有一个线程可以访问共享资源。
  3. 条件变量(Condition Variable)

    条件变量允许一个线程在满足特定条件前进入休眠状态,直到其他线程通知条件变量满足了条件。
  4. 信号量

    信号量不仅可以用于多进程,也可以在多线程中使用,用于控制对有限资源的访问。
  5. 屏障(Barrier)

    屏障允许一组线程在达到某个点前全部等待,然后同时继续执行,用于同步多个线程的执行步骤。
  6. 线程安全队列

    特殊设计的数据结构,例如线程安全的队列,可以用来在线程间安全地传递数据。


五、总结

    多进程和多线程各有优劣,选择合适的并发模型取决于应用的需求、性能要求和安全考虑。在实际开发中,通常会根据具体的场景来决定是采用多进程还是多线程,有时甚至两者结合使用,以充分利用系统资源和提升应用程序的效率。

1.定义和概念

    多进程指的是同时运行多个独立的进程,每个进程有自己的地址空间,是系统分配资源和调度的基本单位。每个进程都有自己的内存空间,数据独立,通信通过进程间通信(IPC)来实现。

多线程是在同一个进程中同时运行多个线程,每个线程共享进程的地址空间和资源,是CPU调度的基本单位。线程之间共享相同的上下文,可以方便地共享数据和通信。

2. 内存和资源管理

  • 每个进程有独立的内存空间,相互之间不受影响,但创建和销毁进程的开销比较大,因为需要分配和释放独立的内存空间。进程间的通信比较复杂,需要使用IPC机制来实现,例如管道、消息队列、共享内存等。

  • 线程共享进程的内存空间,因此数据共享和通信比较容易和高效,但需要确保线程安全,避免数据竞争和死锁等问题。创建和销毁线程的开销相对较小,因为不需要像进程那样分配和释放独立的内存空间。

3. 切换和调度

  • 进程切换的开销比较大,因为涉及到不同进程间的上下文切换,需要保存和恢复更多的状态信息。进程之间的调度由操作系统负责,进程间的切换是通过操作系统的调度算法来实现的。

  • 线程切换的开销相对较小,因为线程共享同一进程的地址空间,上下文切换主要涉及寄存器值的保存和恢复。线程的调度可以由操作系统调度器完成,也可以通过线程库中的用户级调度器来实现。

4. 并发性和效率

  • 由于每个进程都有独立的内存空间,可以更好地利用多核处理器,适合CPU密集型任务。但进程间的通信成本较高,因此在需要频繁通信和协作的场景下效率可能较低。

  • 线程之间共享数据和内存空间,适合IO密集型任务和需要频繁通信的场景。但需要注意线程安全问题,合理设计和使用锁和同步机制,避免数据竞争和死锁。

5. 编程模型和适用场景

  • 多进程适合需要在不同进程间分配任务或者需要独立处理的任务,例如服务器架构中的分布式处理。可以更好地利用多核处理器,提高整体系统的并行性能。

  • 多线程适合需要实时性和相互协作的任务,例如GUI应用程序、Web服务器处理请求、多媒体应用等。可以简化数据共享和通信,提升程序的响应速度和用户体验。

标签:共享内存,int,线程,进程,shared,多线程
From: https://blog.csdn.net/cl122763974/article/details/139873880

相关文章

  • 【C语言】线程同步
    【C语言】线程同步线程同步1.互斥锁2.读写锁3.条件变量4.信号量最后线程同步  线程同步是指在多线程的情况下,如果多个线程去访问共享资源,需要按照一定规则顺序依次去访问,保证共享资源的数据一致性。1.互斥锁互斥相关函数//互斥量pthread_mutex_tmutex;......
  • Linux 7种 进程间通信方式
    传统进程间通信       通过文件实现进程间通信必须人为保证先后顺序       A--->硬盘--->B(B不知道A什么时候把内容传到硬盘中)1.无名管道2.有名管道3.信号IPC进程间通信4.消息队列5.共享内存6.信号灯集7.socket通信一、无名管道(亲缘关系的进程  ......
  • python基础 - 多线程技术
    基础概念importtimedeftest(something):print(f"我开始>>>{something}")time.sleep(2)print(f"我结束>>>{something}")"""场景:1-io密集型--阻塞sleeprequestssocket"""importthreading"&quo......
  • 【Linux】进程间通信_1
    文章目录七、进程间通信1.进程间通信分类管道未完待续七、进程间通信进程间由于进程具有独立性,所以不可以直接进行数据传递。但是我们通常需要多个进程协同,共同完成一件事,所以我们需要进程间通信的手段。进程间通信的本质就是先让不同的进程看到同一份资源,这个......
  • 【七】【QT开发应用】跨UI发送信号,跨线程发送信号
    跨UI发送信号基本框架新建窗口自定义信号跨线程发送信号新建线程查看线程号完整代码跨UI发送信号setdialog.h#ifndefSETDIALOG_H#defineSETDIALOG_H#include<QDialog>namespaceUi{classsetdialog;}class......
  • 线程间通信方式
    1通信机制:互斥与同步线程的互斥通过线程的互斥锁完成;线程的同步通过无名信号量或者条件变量完成。2 互斥2.1何为互斥?       互斥是在多个线程在访问同一个全局变量的时候,先让这个线程争抢锁的资源,那个线程争抢到资源,它可以访问这个变量,没有争抢到资源的线程不......
  • 【2024最新精简版】线程安全/多线程 面试篇
    文章目录一.线程基础线程和进程什么是进程什么是线程并发与并行的区别创建线程继承Thread类实现Runable接口实现Callable接口使用线程池线程状态等待唤醒机制等待方法唤醒方法二.线程池线程池作用创建线程池线程池任务调度流程阻塞队列BlockingQueue线程池拒绝策......
  • Linux的学习与使用(进程管理)
    命令学习(一)ps命令1.ps显示当前终端会话的进程。2.psaux显示系统上所有进程的详细信息。NameDescriptionExample(图中第一行)USER进程的所有者rootPID进程ID1%CPU进程占用的CPU百分比0.2%MEM进程占用的内存百分比0.2VSZ进程使用的虚拟内存量......
  • 进程切换分析(2):TLB处理
    一、前言进程切换是一个复杂的过程,本文不准备详细描述整个进程切换的方方面面,而是关注进程切换中一个小小的知识点:TLB的处理。为了能够讲清楚这个问题,我们在第二章描述在单CPU场景下一些和TLB相关的细节,第三章推进到多核场景,至此,理论部分结束。在第二章和第三章,我们从基本的逻辑......
  • 操作系统的发展史、多道技术、进程理论、进程的三状态、同步异步/阻塞与非阻塞、开启
    【操作系统发展史】1为什么要使用操作系统呢?2程序员无法把所有的硬件操作细节都了解到,管理这些硬件并且加以优化使用是非常繁琐的工作,3这个繁琐的工作就是操作系统来干的,有了他,程序员就从这些繁琐的工作中解脱了出来,4只需要考虑自己的应用软件的编写就可以了,应用软件......