unix多进程多线程

概要

线程和进程是两种不同的并发执行单元,它们都可以实现多任务的执行,但是有各自的特点和区别。

  • 线程是操作系统独立调度和执行的基本单位,它是进程的一个组成部分,一个进程可以包含多个线程,它们共享进程的地址空间、数据和资源。线程有自己的栈、寄存器和程序计数器,它们之间可以直接通信和协作。
  • 进程是程序在某个数据集合上的一次运行活动,也是操作系统进行资源分配和保护的基本单位。进程有自己独立的地址空间、数据和资源,进程之间通过通信机制(如管道、信号、消息队列等)来交换信息。

要调用线程和进程,需要使用不同的方法,具体取决于操作系统和编程语言。一般来说,有以下几种常见的方法:

  • 在 Linux 系统中,可以使用 fork() 函数来创建一个新的进程,该函数会复制当前进程的地址空间、数据和资源,并返回一个进程标识符(PID)。父进程和子进程可以通过 PID 来区分自己,并继续执行不同的代码。可以使用 wait() 函数来等待子进程结束,并获取其返回值。可以使用 exec() 系列函数来在子进程中执行另一个程序,并替换其地址空间、数据和资源。
  • 在 Linux 系统中,可以使用 pthread 库来创建和管理线程。pthread 库提供了一系列函数来创建、终止、同步、互斥和调度线程。可以使用 pthread_create() 函数来创建一个新的线程,并指定其要执行的函数和参数。可以使用 pthread_join() 函数来等待一个线程结束,并获取其返回值。可以使用 pthread_exit() 函数来终止当前线程,并返回一个值。
  • 在 Windows 系统中,可以使用 CreateProcess() 函数来创建一个新的进程,并指定其要执行的程序、命令行参数、安全属性、优先级等。该函数会返回一个进程句柄(HANDLE),用于标识和控制该进程。可以使用 WaitForSingleObject() 函数来等待一个进程结束,并获取其退出码。可以使用 TerminateProcess() 函数来强制终止一个进程,并指定其退出码。
  • 在 Windows 系统中,可以使用 CreateThread() 函数来创建一个新的线程,并指定其要执行的函数和参数。该函数会返回一个线程句柄(HANDLE),用于标识和控制该线程。可以使用 WaitForSingleObject() 函数来等待一个线程结束,并获取其退出码。可以使用 ExitThread() 函数来终止当前线程,并返回一个值。
  • 在 C 语言中,可以使用 system() 函数来调用操作系统命令,并创建一个新的进程来执行该命令。该函数会阻塞当前进程,直到命令执行完毕,并返回命令的退出码。
  • 在 C++ 语言中,可以使用 std::thread 类来创建和管理线程。std::thread 类提供了构造函数、析构函数、移动赋值运算符等方法来创建、销毁、转移线程对象。可以向构造函数传递一个可调用对象(如函数指针、函数对象、lambda 表达式等)和参数,来指定线程要执行的任务。可以使用 join() 方法来等待一个线程结束,并获取其返回值。可以使用 detach() 方法来分离一个线程,使其在后台运行。

进程和线程的概念

  • 进程是程序在某个数据集合上的一次运行活动,也是操作系统进行资源分配和保护的基本单位。
  • 线程是进程中的一个执行单元,是操作系统进行调度的基本单位。
    进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。
  • 而线程是共享进程中的数据的,使用相同的地址空间,因此 CPU 切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。

进程和线程的状态转换

  • 进程有三种基本状态:就绪态、运行态和阻塞态。就绪态表示进程已经准备好运行,等待 CPU 分配时间片;运行态表示进程正在占用 CPU 执行;阻塞态表示进程因为等待某些事件而暂停执行,让出 CPU 资源。
  • 线程也有类似的状态转换,但是线程的阻塞不会导致整个进程的阻塞,同样线程的就绪也不一定会立即运行。
  • 进程和线程的状态转换都需要操作系统的干预,操作系统通过进程控制块(PCB)来管理和调度进程,通过原语来实现进程状态转换的功能。

进程和线程的通信方式

  • 进程间通信(IPC)需要借助操作系统提供的服务,常见的方式有管道、消息队列、信号量、共享内存、套接字等。
  • 线程间通信(TIC)由于共享同一进程的资源,可以直接读写数据,无需操作系统干预。但是为了避免数据不一致或冲突,需要使用互斥锁、信号量等同步机制来保证线程安全。

线程共享进程的资源

  • 线程是进程的一部分,它们在同一个进程内共享同一内存空间和其他资源。

  • 线程之间共享的资源包括:

    • 进程代码段:这里保存的是编译后的可执行机器指令,所有线程都可以访问和执行相同的程序逻辑。
    • 进程的公有数据:这里保存的是全局变量和静态变量,所有线程都可以直接读写这些数据。
    • 进程打开的文件描述符:如果一个线程打开了文件,其他线程也可以访问该文件的数据。
    • 信号处理器:进程中的所有线程共享信号处理程序,当进程收到信号时,所有线程都会受到影响。
    • 进程的当前目录和进程用户ID与进程组ID:这些是进程的状态信息,所有线程都共享。
  • 线程之间不共享的资源包括:

    • 栈区:每个线程都有自己独立的栈区,用来保存函数运行时的信息,如返回值、参数、局部变量、寄存器等。虽然没有严格的隔离机制,但一般不建议一个线程修改另一个线程的栈区数据。
    • 程序计数器:每个线程都有自己的程序计数器,用来记录下一条要执行的指令地址。
    • 线程状态:每个线程都有自己的状态,如就绪、运行、阻塞等。

C++中的线程进程调用

C++11标准提供了头文件来支持多线程编程,可以使用std::thread类来创建和管理线程。

下面是一些C++中的线程或进程调用示例:

  • 创建一个简单的线程,执行一个无参数的函数:
#include <iostream>
#include <thread>

void thread_fun() // 线程要执行的函数
{
    std::cout << "Hello, thread!" << std::endl;
}

int main()
{
    std::thread t(thread_fun); // 创建一个线程对象t,并传入要执行的函数
    t.join(); // 等待线程t结束
    return 0;
}
  • 创建一个带参数的线程,执行一个有参数的函数:
#include <iostream>
#include <thread>

void thread_fun(int x) // 线程要执行的函数,有一个整型参数
{
    std::cout << "The parameter is " << x << std::endl;
}

int main()
{
    std::thread t(thread_fun, 42); // 创建一个线程对象t,并传入要执行的函数和参数
    t.join(); // 等待线程t结束
    return 0;
}
  • 创建多个线程,共享同一个函数,并使用互斥锁来保护共享数据:
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx; // 创建一个互斥锁对象
int counter = 0; // 创建一个共享数据

void thread_fun() // 线程要执行的函数
{
    mtx.lock(); // 上锁,保护共享数据
    counter++; // 修改共享数据
    std::cout << "Thread " << counter << " is running" << std::endl;
    mtx.unlock(); // 解锁,释放共享数据
}

int main()
{
    const int N = 10; // 定义线程数量
    std::thread threads[N]; // 创建一个线程数组

    for (int i = 0; i < N; i++)
    {
        threads[i] = std::thread(thread_fun); // 为每个线程对象分配要执行的函数
    }

    for (int i = 0; i < N; i++)
    {
        threads[i].join(); // 等待每个线程结束
    }

    return 0;
}
  • 在Linux系统中,创建一个新的进程,并在子进程中执行另一个程序:
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    pid_t pid = fork(); // 创建一个新的进程,返回进程标识符

    if (pid == -1) // fork失败
    {
        std::cerr << "Fork error" << std::endl;
        return -1;
    }
    else if (pid == 0) // 子进程
    {
        std::cout << "Child process, pid = " << getpid() << std::endl;
        execl("/bin/ls", "ls", "-l", NULL); // 在子进程中执行ls命令,替换子进程的地址空间、数据和资源
        std::cerr << "Exec error" << std::endl; // 如果exec成功,这一行不会被执行
        return -1;
    }
    else // 父进程
    {
        std::cout << "Parent process, pid = " << getpid() << std::endl;
        int status;
        wait(&status); // 等待子进程结束,并获取其返回值
        std::cout << "Child process exited with status = " << status << std::endl;
        return 0;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值