01_C++多线程简介

发布时间:2023年12月18日

学习内容:

  1. 什么是多线程
  2. 多线程和多进程的区别
  3. 常见的<thread>中的函数
  4. 最基本的创建线程的方式

简介

  • 传统的C++(C++11标准之前)中并没有引入线程这个概念,之前如果我们想要在C++中实现多线程,需要借助操作系统平台提供的API,比如Linux的<pthread.h>,或者windows下的<windows.h> 。
  • C++11提供了语言层面上的多线程,包含在头文件中。它解决了跨平台的问题,提供了管理线程、保护共享数据、线程间同步操作、原子操作等类。C++11 新标准中引入了5个头文件来支持多线程编程。
头文件
1. <thread>:提供了多线程编程的基本功能,包括创建线程、线程的启动和终止,以及线程间的通信和同步。
2. <mutex>:提供了互斥锁和其他线程间同步原语,用于确保多个线程可以安全地访问共享资源。它包括互斥锁、递归锁、读写锁、条件变量等。
3. <condition_variable>:提供了条件变量机制,用于在线程之间进行条件通知和等待。它可以用于实现线程的等待和唤醒操作。
4. <future>:提供了异步编程的支持,可以让一个线程在后台执行一个任务,并返回一个异步的结果。它包括std::async、std::future、std::promise等。
5. <atomic>:提供了原子操作的支持,用于保证多个线程对共享变量的操作的原子性。它包括原子类型和原子操作函数。

多线程和多进程的区别:

  • 进程是指多个可执行文件(exe)同时运行。每个进程都是独立的执行单元,具有自己的内存空间、资源和代码,它们之间相互独立运行,并通过操作系统提供的进程间通信机制来进行数据交互和协作。多个进程可以在操作系统的调度下并发执行,每个进程都有自己的运行状态和执行轨迹。
  • 线程是进程的执行单位,一个进程可以包含多个线程,这些线程共享同一个进程的资源,如内存空间、文件句柄等。每个线程都有自己的栈空间和程序计数器,但共享进程的堆空间和静态变量。
多线程和多进程是实现并发编程的两种主要方式,它们有以下区别:
  1. 资源占用:多线程共享同一进程的资源,而多进程各自拥有独立的资源。因此,多线程的开销较小,但需要更加注意线程间的同步和资源竞争问题;而多进程的开销较大,但拥有更好的隔离性和稳定性。
  2. 线程间通信:多线程之间可以通过共享内存来进行通信,因为它们共享相同的内存地址空间。多进程之间通信的方式较多,可以使用管道、消息队列、共享内存等。
  3. 创建和切换开销:创建和切换线程的开销较小,因为它们只需要保存和切换栈和寄存器等少量资源。而创建和切换进程的开销较大,因为需要为每个进程分配独立的资源。
  4. 并发能力:多进程具有更好的并发能力,因为每个进程都有独立的执行环境,可以在多个CPU上并行执行。而多线程的并发性依赖于操作系统的调度器,只能在一个CPU上执行。
  5. 编程模型:多线程使用共享内存模型,多个线程可以直接访问和修改共享的数据,但需要注意线程安全;多进程使用消息传递模型,进程间通信需要通过特定的方式传递消息。
    总的来说,多线程适合于在同一个进程中进行并发操作和数据共享,具有较小的开销和较好的效率;而多进程适合于独立运行的任务,可以充分利用多核CPU的并行能力,但需要更多的资源开销和通信机制。选择使用哪种方式取决于具体需求和问题的性质。

使用方式

线程的创建

创建线程只需要把函数添加到线程当中即可。
形式1:

std::thread myThread (thread_fun);
//函数形式为void thread_fun()
myThread.join();
//同一个函数可以代码复用,创建多个线程

形式2:

std::thread myThread (thread_fun(100));
myThread.join();
//函数形式为void thread_fun(int x)
//同一个函数可以代码复用,创建多个线程

形式3:

std::thread (thread_fun,1).detach();
//直接创建线程,没有名字
//函数形式为void thread_fun(int x)
/*
std::thread(thread_func, 1).detach();的意思是创建一个新线程,该线程执行thread_func函数,并将数字1作为参数传递给thread_func函数。然后,通过调用detach()函数,将该线程与当前线程分离,使其成为一个独立的后台线程。
*/

1. thread

  • 使用方式:
    包含头文件:
#include <iostream>
#include <thread>
  • 创建线程函数
void myThreadFunc() {
    // 线程要执行的代码
    std::cout << "Thread executing" << std::endl;
}
  • 在主函数中创建线程并启动。
int main() {
    // 创建线程
    std::thread myThread(myThreadFunc);
    // 启动线程
    myThread.join();
    return 0;
}

2. thread中join 和detach区别

std::thread类中的join()detach()函数是用于控制线程的结束和资源管理的两种方式。

  1. join()函数:
    • 调用join()函数将等待线程执行完成,并阻塞当前线程,直到被等待的线程结束。
    • 调用join()函数后,将无法再使用被等待线程的std::thread对象。
    • 使用join()函数可以确保线程的完成,并避免线程被提前销毁导致的资源泄漏。
  2. detach()函数:
    • 调用detach()函数将分离线程,使得线程能够在后台继续执行,与主线程并行。
    • 调用detach()函数后,被分离的线程将在执行完毕后自行销毁,无需显式地等待。
    • 使用detach()函数应该确保线程能够独立运行,并且不再需要对其进行管理或等待。
      需要注意的是,一旦调用了detach()函数,就无法再使用join()函数等待线程的结束。如果没有调用join()detach()函数,而直接析构了std::thread对象,则程序会终止并报错。
    • 可以使用joinable判断是join模式还是detach模式。
    • if (myThread.joinable()) foo.join();

在选择join()detach()时,需要根据具体情况判断是否需要等待线程的完成和资源的管理。

文章来源:https://blog.csdn.net/weixin_44248637/article/details/135059937
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。