线程池 c++11
左值引用与右值引用 & 与 &&
在C++中,左值(Lvalues)和右值(Rvalues)是用于描述表达式和对象的两个基本概念。
-
左值(Lvalues):
- 左值是指在内存中有确定位置的表达式或对象。它们可以出现在赋值语句的左边。
- 左值可以被取地址,即可以使用取地址运算符
&
获取它们的地址。 - 典型的左值包括变量、数组元素、类成员等。
cppCopy codeint x = 10; // x是左值 int arr[5]; // arr是左值
-
右值(Rvalues):
- 右值是指在内存中没有确定位置的临时表达式或对象。它们通常出现在赋值语句的右边。
- 右值不能被直接取地址,即不能使用取地址运算符
&
获取它们的地址。 - 典型的右值包括常量、临时表达式、字面值等。
cppCopy codeint result = 5 + 3; // 5 + 3是右值 int&& temp = 10; // 10是右值(通过引用绑定)
C++11引入了右值引用(Rvalue References),允许我们对右值进行引用绑定,从而更灵活地处理右值。右值引用使用&&
来声明:
cppCopy code
int&& rvalueRef = 5; // 右值引用绑定到右值
总体而言,左值和右值的概念在C++中与内存中的对象的生命周期和可修改性密切相关。左值通常是可以被取地址、有名字的对象,而右值通常是临时的、不可取地址的值。
使用 “&” 表示。但此种引用方式有一个缺陷,即正常情况下只能操作 C++ 中的左值,无法对右值添加引用int &c = 10; //错误,但允许使用常量左值引用操作右值const int &c = 10;
C++11 标准新引入了另一种引用方式,称为右值引用,用 “&&” 表示。&&右值引用(Rvalue reference)是指绑定到右值(Rvalue)的引用类型,通常使用&& 符号声明。
C++11右值引用(一看即懂) (biancheng.net)
std::move
std::move
是 C++ 中 <utility>
头文件中提供的一个函数模板。它主要用于将左值(Lvalues)转换为右值引用(Rvalue References),从而支持移动语义。移动语义是 C++11 引入的一项特性,它允许高效地将资源(如内存所有权)从一个对象转移到另一个对象,而不进行深拷贝。
std::queue<std::function<void()>> tasks;
auto task = tasks.front();与std::function<void()> task(std::move(tasks.front()));的特点和区别
auto task = tasks.front();
会将 task
初始化为 tasks
队列的前端元素的拷贝。这意味着 task
是 tasks.front()
的一个副本,而不会影响队列本身。
std::function<void()> task(std::move(tasks.front()));
在这里,std::move(tasks.front())
将 tasks
队列的前端元素转换为右值引用,并且通过 std::function<void()> task(...)
的构造函数使用移动构造函数。这样做的主要优势是,如果 std::function
支持移动语义(通常是支持的),那么它可以更有效地转移 tasks.front()
中的资源而不进行不必要的复制。
在处理队列元素时,选择使用 std::move
取决于你是否希望在移动语义下进行操作以提高性能。使用 std::move
通常是在你明确知道你不再需要原始元素时,以及你希望避免不必要的复制时。
完美转发 std::forward<>
定义:如果我们需要一种方法能够按照参数原来的类型转发到另一个函数,这种转发类型称为完美转发。
一个右值引用参数作为函数的形参,在函数内部再转发该参数的时候它已经变成一个左值,并不是他原来的类型:
当我们将一个右值引用传入函数时,他在实参中有了命名,所以继续往下传或者调用其他函数时,根据C++ 标准的定义,这个参数变成了一个左值。那么他永远不会调用接下来函数的右值版本,这可能在一些情况下造成拷贝。为了解决这个问题 C++ 11引入了完美转发,根据右值判断的推倒,调用forward 传出的值,若原来是一个右值,那么他转出来就是一个右值,否则为一个左值。
这样的处理就完美的转发了原有参数的左右值属性,不会造成一些不必要的拷贝。代码如下:
template<typename T>
void print(T& t) {
t = 1;
//std::cout << t << std::endl;
std::cout << "lvalue" << std::endl;
}
template<typename T>
void print( T&& t) {
t =2;
/*std::cout <<t << std::endl;*/
std::cout << "rvalue" << std::endl;
}
template<typename T>
void TestForward(T&& v) {
print(v);
std::cout << v << std::endl << std::endl;
print(std::forward<T>(v));
std::cout << v << std::endl << std::endl;
print(std::move(v));
std::cout << v << std::endl << std::endl;
}
int main() {
TestForward(0);
int x = 1;
TestForward(x);
TestForward(std::forward<int>(x));
return 0;
}
函数中&&为万能引用,既左值、右值都能接收,但&作为函数形参时,只能接受左值。
简单线程池
www.seestudy.cn
#include<iostream>
#include<thread>
#include<condition_variable>
#include<queue>
#include<mutex>
#include<functional>
class ThreadPool
{
private:
bool stop;
std::queue<std::function<void()>> tasks;
std::vector<std::thread> threads;
std::mutex mtx;
std::condition_variable cv;
public:
ThreadPool(int numThreads) :stop(false) {
for (int i = 0; i < numThreads; ++i) {
std::cout << "thread" << i << std::endl;
//threads.emplace_back([this]() {
threads.push_back(std::thread([this]() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return !tasks.empty() || stop; });
//cv.wait(lock, !tasks.empty()||stop);
if (stop && tasks.empty()) return;
//auto task = tasks.front();
std::function<void()> task(std::move(tasks.front()));
tasks.pop();
lock.unlock();
task();//执行任务,提前将任务bind成函数了,不用传参数
}
}));
}
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(mtx);
stop = false;
}
for (auto& t : this->threads) {
t.join();
}
}
//左值是指可以放在等号左边的值,反之,只能位于赋值号右侧的表达式就是右值:常数、
// 使用 "&" 表示。但此种引用方式有一个缺陷,即正常情况下只能操作 C++ 中的左值,无法对右值添加引用int &c = 10; //错误,但允许使用常量左值引用操作右值const int &c = 10;
// C++11 标准新引入了另一种引用方式,称为右值引用,用 "&&" 表示。
// &&右值引用(Rvalue reference)是指绑定到右值(Rvalue)的引用类型,通常使用&& 符号声明。右值是指不能被取地址的表达式,例如临时对象、表达式结果等等。
// 和常量左值引用不同的是,右值引用还可以对右值进行修改
//这里传入的是lambda表达式或者函数,
template<typename F, typename ...Args>
void enqueue(F&& f, Args&&... args)
{
//std::function<void()> task(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::function<void()> task(std::bind(f, args...));
{
std::unique_lock<std::mutex> lock(mtx);
//tasks.emplace(std::move(task));
tasks.emplace(task);
}
cv.notify_one();
}
};
int main()
{
ThreadPool tp(5);
std::this_thread::sleep_for(std::chrono::microseconds(100));
std::cout << "cjwxcc" << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(10));
for (int i = 0; i < 10; ++i)
{
auto task = [i]() {std::cout << "Task" << i << "is runing" << std::endl; };
tp.enqueue(task);
}
return 0;
}