• std::thread线程详解


    目录

    简介

    本文主要介绍了标准库中的线程部分。线程是目前多核编程里面最重要的一部分。
    与进程进程相比,其所需的资源更少,线程之间沟通的方法更多; 他们之间的区别可以比较简明用以下几点概括[1]:

    1. 进程是资源分配的最小单位,线程是CPU调度的最小单位;也就是说进程之间的资源是相互隔离,而线程之间的是可以相互访问的。
    2. 线程的存在依赖于进程,一个进程可以保护多个线程;
    3. 进程出现错误不会影响其他进程,但是一个线程出现错误,会影响同一进程下的所有线程。

    线程的使用

    线程的创建

    一般使用std::thread创建一个线程。std::thread支持输入一个函数对象,及一些参数,类似于std::bind,不过没有占位符。

    最常见,最简单的是对输入一个匿名函数作为参数:

    1
    2
    3
    4
    std::thread t1([]() {
      std::cout << "Hello World" << std::endl;
    });
    t1.join();
    

    需要注意的是,在使用多线程的时候,如果使用类似于std::cout << "Hello World" << std::endl;的语句,容易造成输出的混乱。比如

    1
    2
    3
    4
    5
    6
    7
    8
    std::thread t1([]() {
      std::cout << "Hello World1" << std::endl;
    });
    std::thread t2([]() {
      std::cout << "Hello World2" << std::endl;
    });
    t1.join();
    t2.join();
    

    以上代码,我们一般来说期望的输出是

    期望的输出

    但是,在一些情况下,它还会以以下的方法输出

    错误的输出

    造成这个的原因很简单,因为"Hello World"std::endl的输出是分开的,所以他们之间可能被插入其他的输出。为了解决这个问题。一般会使用一个完整的字符串进行输出,但是C++在格式化这一方面做的比较差(C++20format库看起来还不错),所以一般情况下会使用printf输出。

    线程的方法和属性

    1. joinable()判断线程是否可连接(可执行线程)的,有以下情况的,为不可连接的:

      1. 构造时,thread()没有参数;
      2. 该对象的线程已经被移动了;
      3. 该线程已经被joindetach
    2. get_id() 返回线程的ID;

    3. native_handle() 返回POSIX标准的线程对象;

    4. join() 等待线程执行完成;

    5. detach() 分离线程,分离后对象不再拥有线程。该线程结束后,会自动回收内存。(并不会开启另一个进程);

    6. swap() 交换对象的线程。

    std::jthread (C++20)

    除了常用的std::thread外,标准库还存在着另一个可以创建线程的类,std::jthread。他们之间的差别比较明显的就是,std::jthread会在解构的时候判断线程是否还在运行joinable,如果还在运行则自动调用request_stopjoin

    除此之外,std::jthread还提供了一个内置的std::stop_token。可以通过线程函数的第一个参数来获取(如果函数的第一个参数类型为std::stop_token)。

    可以通过get_stop_sourceget_stop_tokenrequest_stop等方法来对其进行操作。

    stop_token (C++20)

    stop_token类似于一个信号,告诉线程是否到了结束的时候。和stop_source一起使用。stop_token用来获取是否退出(读),而stop_source用来请求推出(读写)。其方法:

    1. request_stop 请求退出

    2. stop_requested 获取是否已经请求退出

    3. stop_possible 获取是否可以请求退出

    样例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    void thread_func(std::stop_token token) {
        int data = 0;
        while (!token.stop_requested()) {
            printf("%d
    ", data);
            data++;
            std::this_thread::sleep_for(1s);
        }
        printf("Exit
    ");
    }
    
    int main() {
        std::jthread mythread(thread_func);
    
        std::this_thread::sleep_for(4s);
    
        return 0;
    }
    

    输出:

    jthread

    总结

    本次讲述了线程创建的一些方法,可以看到相比较C语言而言,由于C++11提出的函数对象(普通函数、匿名函数,std::bind的输出等)使得线程的创建更加的方便。

    下一次将讲述线程之间的通信。在C++中,线程之间的通信方法和C语言提供的类似,不过是将其包装了一下。

  • 相关阅读:
    Java并发基础10:原子性操作类的使用
    Java并发基础09. 多个线程间共享数据问题
    Java并发基础08. 造成HashMap非线程安全的原因
    Java并发基础07. ThreadLocal类以及应用技巧
    Java并发基础06. 线程范围内共享数据
    Java并发基础05. 传统线程同步通信技术
    Java并发基础04. 线程技术之死锁问题
    我是如何从通信转到Java软件开发工程师的?
    IOS 判断耳机插入/拔出
    海量数据处理
  • 原文地址:https://www.cnblogs.com/csk001/p/14222543.html
Copyright © 2020-2023  润新知