代码编织梦想

1. thread是什么

多线程是一种功能,它允许并发执行程序的两个或多个部分,以最大限度地利用 CPU。这种程序的每个部分都称为线程。因此,线程是进程中的轻量级进程。多线程支持是在 C++11 中引入的。在 C++11 之前,我们必须使用 POSIX 线程或库。虽然这个库完成了这项工作,但缺乏任何标准语言提供的功能集导致了严重的可移植性问题。C++ 11 取消了所有这些,并给了我们 std::thread。线程类和相关函数在头文件<thread>中定义。

thread表示单个执行线程。线程在构建关联的线程对象时立即开始执行。其定义用于观察和管理应用程序中的执行线程的对象。

2. 创建一个thread

std::thread 是 C++ 中表示单个线程的线程类。要启动线程,我们只需要创建一个新的线程对象,并将要调用的执行代码(即可调用对象)传递到对象的构造函数中。

//当程序到达此行时,将在后台启动任务以运行aFunction 
//t:线程对象
//aFunction: 任务或线程执行
std::thread t(aFunction);

创建对象后,将启动一个新线程,该线程将执行 aFunction 中指定的代码。可调用对象可以是以下五个项中的任何一个:

  • 函数指针
  • Lambda 表达式
  • 函数对象
  • 非静态成员函数
  • 静态成员函数

定义可调用对象后,我们将其传递给构造函数。我们看以下例子:

/*****************1.使用函数指针启动线程********************/

//函数指针可以是可调用对象,传递给 std::thread 构造函数以初始化线程。
void foo(param)
{ 
   ... 
}
// The parameters to the function are put after the comma
std::thread thread_obj(foo, params);
/********************************************************/


/***************2.使用 Lambda 表达式启动线程****************/

//定义一个lambda表达式
auto f = [](params)
{
	...
};

//使用 lambda 表达式作为可调用对象来启动
std::thread thread_object(f, params);
/********************************************************/


/******************3.使用函数对象启动线程*******************/

// 定义一个函数对象
class fn_object_class {
	// 重载operator()
	void operator()(params)
	{ 
	   ...
	}
}

std::thread thread_object(fn_object_class(), params);
/********************************************************/


/***************4.使用非静态成员函数启动线程****************/

// 定义一个类
class Base {
public:
	// 非静态成员函数
	void foo(param) { ... }
}

//创建Base类对象b
Base b;

// 第一个参数是类非静态成员函数的引用
// 第二个参数类对象的引用
// 第三个参数是非静态成员函数的参数
std::thread thread_obj(&Base::foo, &b, params);
/********************************************************/


/***************5.使用静态成员函数启动线程****************/

// 定义一个类
class Base {
public:
	//静态成员数
	static void foo(param) { ... }
}

//创建Base类对象b
Base b;
// 其一个参数是类静态成员函数的引用
// 第二个参数是该函数的参数
std::thread thread_obj(&Base::foo, params);
/********************************************************/

:我们总是将可调用对象的参数作为参数单独传递给线程构造函数。

3. 等待线程执行完毕

线程启动后,我们可能需要等待线程完成,然后才能采取一些操作。要等待线程,请使用 std::thread::join() 函数。此函数使当前线程等待,直到*this标识的线程完成执行。

int main()
{
	//开始一个线程t1
	std::thread t1(callable);

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

	//t1完成后再继续执行其他语句
	...
}

有时我们需要知道线程对象是否可连接,即它与活动任务相关联。我们可以通过函数对其进行 joinable() 评估:

if (t.joinable())
    t.join();

其主要是检查 std::thread 对象是否标识活跃的执行线程。具体而言,若 get_id() != std::thread::id() 则返回true。故默认构造的 thread 不可结合。

std::thread::get_id返回线程的 id,即返回标识与 *this 关联的线程的std::thread::id

如果线程是 joinable ,并不意味着它已完成。它可能仍在运行。因此,如果我们调用 join() 一个 joinable 任务,它只有在它已经完成时才加入,否则,程序会等待它完成,然后加入。

4. 线程任务的移动和交换

没有两个 std::thread 对象会表示同一执行线程,因为 std::thread可移动构造可移动赋值,但不是可复制构造可复制赋值的。例如:

auto task(){/* 某些计算过程 */}
std::thread t1(task);
std::thread t2 = t1; //错误: 线程不可以复制
std::thread t3{t1}; // 错误: 线程不可以拷贝构造

//一次只有一个线程对象负责一个任务。但是,与线程对象关联的任务是可移动的:
std::thread t4 = std::move(t1); //正确: t4现在运行task,t1变成一个空对象

std::thread::swap成员函数可以交换两个 thread 对象,其实就是交换二个 thread 对象的底层柄。其函数原型如下:

void swap( std::thread& other ) noexcept; //C++11 起

除了可以使用成员函数外,也可以使用非成员数std::swap(std::thread) 来完成两个线程对象的交换。

void foo(){ ... }
void bar(){ ... }
std::thread t1(foo);
std::thread t2(bar);

//交换t1和t2
std::swap(t1, t2); //等效于t1.swap(t2);

5. 线程对象与任务分离

线程对象可以与其任务分离。这对于需要在后台运行的任务非常有用,我们不需要在运行时停止它。

std::thread::detach容许线程从线程句柄独立开来执行,其从 thread 对象分离执行线程,允许执行独立地持续。一旦该线程退出,则释放任何分配的资源。调用 detach*this不再占有任何线程。

示例:

#include <iostream>
#include <chrono>
#include <thread>
 
void independentThread() 
{
    std::cout << "Starting concurrent thread.\n";
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Exiting concurrent thread.\n";
}
 
void threadCaller() 
{
    std::cout << "Starting thread caller.\n";
    std::thread t(independentThread);
    t.detach();
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Exiting thread caller.\n";
}
 
int main() 
{
    threadCaller();
    std::this_thread::sleep_for(std::chrono::seconds(5));
}

可能的输出:

Starting thread caller.
Starting concurrent thread.
Exiting thread caller.
Exiting concurrent thread.

文章首发公众号:iDoitnow如果喜欢话,可以关注一下

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jianmo1993/article/details/134217076

C++ 多线程(3)std::thread 详解-爱代码爱编程

文章目录 一、头文件二、std::thread 构造函数三、其他成员函数四、传递临时参数作为线程对象的注意事项4.1 **解决办法:**4.2 原因分析4.3 总结五、传递类对象、智能指针作为线程参数5.1 修改子线程中的对象,不会影响主线程中的对象5.2 传递智能指针参考链接:打赏 一、头文件 std::thread 在 头文件中声明,因此

C++多线程 -- std::thread的基本用法-爱代码爱编程

依赖头文件: #include< thread > 用法: std::thread 和 join或 detach一起用 std::thread t1(调用函数名称,调用函数参数1,调用函数参数2,。。。,调用函数参数n) t1.join(); //表示同步(阻塞),调用线程走完,才能走后面的流程 t1.detach(); //表示异步,主线程只

C++ 进程与线程---std::thread()-爱代码爱编程

目录 thread函数定义 thread创建线程  成员函数 传递临时参数作为线程对象的注意事项 解决方案1 thread函数定义 头文件:#include <thread> (1). 默认构造函数,创建一个空的 thread 执行对象。 (2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 j

C++多线程:std::thread-爱代码爱编程

最近这段时间在学习C++多线程相关的知识,打算将学习的内容记录下来,加深理解和记忆。 C++11 新标准中引入了五个头文件来支持多线程编程,他们分别是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。 <atomic>

c++11多线程thread使用详解_long_xu的博客-爱代码爱编程

C++11多线程thread 一、线程thread1.1、语法1.1.1、构造函数1.1.2、主要成员函数 1.2、简单线程的创建1.3、线程封装1.4、std::this_thread1.4.1、std::t

c++:std::thread:线程用法-爱代码爱编程

1:std::thread的基本用法 最简单的 std::thread用法如下,调用 thread将立即同时开始执行这个新建立的线程,新线程的任务执行完毕之后, main()的主线程也会继续执行。 #include<iostream> #include<thread> #include<windows.h>

c++ std::thread 如何使用?_std:thread创建后直接运行吗-爱代码爱编程

C++是一种高级编程语言,被广泛用于开发高性能、大规模、复杂的软件系统。其中一个强大的特性就是多线程编程,而std::thread是C++标准库提供的多线程支持的重要组成部分。 免费虚拟手机号注册网址 std:

std::thread使用及实现原理精讲(全)_std::thread详解-爱代码爱编程

C++进阶专栏:http://t.csdnimg.cn/HGkeZ  相关系列文章: std::thread使用及实现原理精讲(全) 有了std::thread,为什么还需要引入std::jthread? 目录 1.windows创建线程 2.linux创建线程 3._beginthread小融合 4.CreateT