文章目录
线程支持库:https://zh.cppreference.com/w/cpp/thread
1. 普通函数线程化
#include <iostream>
#include <thread>
using namespace std;
// 线程函数
void func(int a, int b)
{
for (int i = 0; i < n; ++i)
{
cout << "thread 1 executing a = " << a << ", b = " << b << endl;// 毫秒
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
int main()
{
int x = 10;
std::thread t1(func, x, 100);
// 睡眠一秒
std::this_thread::sleep_for(std::chrono::seconds(1));
t1.join();// 等待t1线程结束,主线程再结束
return 0;
}
// 进程与线程的区别
/*
Linux : 进程与线程都是调度单位,每个进程都是独立的,
Linux 2.6 主函数 <==> 主线程
*/
1.1 线程具有独立的栈空间
#include <iostream>
#include <thread>
using namespace std;
void func()
{
int x = 1;
for (int i = 0; i < 5; ++i)
{
cout << "thread executing x = " << x++ << endl;// 毫秒
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
// 问题:线程t1与t2执行后,是否可能会输出 x = 6/7/8/9 的情况?
// 答:不会,因为每个线程都拥有自己的栈空间,对于线程内部来说,
// x是在线程内部的局部变量,不会被其他线程共享
int main()
{
std::thread t1(func);
std::thread t2(func);
// 睡眠
std::this_thread::sleep_for(std::chrono::seconds(2));
t1.join();// 等待t1线程结束,主线程再结束
t2.join();// 等待t1线程结束,主线程再结束
return 0;
}
对每个进程,Linux内核都把两个不同的数据结构紧凑的存放在一个单独为进程分配的内存区域中:
一个是内核态的进程堆栈stack
另一个是紧挨着进程描述符的小数据结构thread_info,叫做线程描述符。
这两个结构被紧凑的放在一个联合体中thread_union中,
union thread_union { struct thread_info thread_info; unsigned long stack[THREAD_SIZE/sizeof(long)]; };
这块区域32位上通常是8K=8192(占两个页框),64位上通常是16K,其实地址必须是8192的整数倍。
原文链接:https://blog.csdn.net/gatieme/article/details/51577479
1.2 线程安全:访问全局变量可能会造成的资源竞态问题
#include <iostream>
#include <thread>
using namespace std;
int g_num = 1;
void func()
{
for (int i = 0; i < 5; ++i)
{
cout << "thread executing g_num = " << (g_num)++ << endl;// 毫秒
// g_num ++ 操作的汇编
// move eax, g_num; // 取值操作,将g_num的值取到eax寄存器中
// add eax, 1; // 加一操作,eax 进行加一操作
// mov g_num, eax; // 放值操作,将加一后的值从eax放回g_num中
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
// 问题:此时线程t1与t2执行后,是否可能会输出 x = 6/7/8/9 的情况?
// 答:会,因为每个线程除了各自拥有自己的栈空间外,还共享整个程序的全局空间
//
// 注意:对于全局量 g_num 来说,对主线程、t1线程、t2线程都是可见的。因此,t1线程与t2线程在访问
// g_num 时会出现资源的竞争,如果不加以控制就会造成线程不安全的情况。
// 比如,在我们进行若干次的运行后,输出结果可能出现g_num小于10的情况。
int main()
{
std::thread t1(func);
std::thread t2(func);
// 睡眠
std::this_thread::sleep_for(std::chrono::seconds(2));
t1.join();// 等待t1线程结束,主线程再结束
t2.join();// 等待t1线程结束,主线程再结束
return 0;
}
2. 线程函数的参数传递
2.1 传递指针的线程函数
#include <iostream>
#include <thread>
using namespace std;
void func(int a, int *p)
{
if (nullptr == p) return;
for (int i = 0; i < 5; ++i)
{
cout << "thread a = " << a++
<< " , *(p) = " << (*p)++ << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
int main()
{
int a = 10, b = 20;
std::thread t1(func, a, &b);
std::thread t2(func, a, &b);
// 睡眠
std::this_thread::sleep_for(std::chrono::seconds(2));
t1.join();// 等待t1线程结束,主线程再结束
t2.join();// 等待t1线程结束,主线程再结束
cout << "main: a = " << a << " , b = " << b << endl;
// 输出a,与b 的值是多少?
return 0;
}
2.2 传递引用的线程函数
#include <iostream>
#include <thread>
using namespace std;
void func(int &a)
{
for (int i = 0; i < 5; ++i)
{
cout << "thread a = " << a++ << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
int main()
{
int a = 10;
std::thread t1(func, std::ref(a));
std::thread t2(func, std::ref(a));
// 睡眠
std::this_thread::sleep_for(std::chrono::seconds(2));
t1.join();// 等待t1线程结束,主线程再结束
t2.join();// 等待t1线程结束,主线程再结束
cout << "main: a = " << a << endl;
// 输出a 的值是多少?
return 0;
}
当线程函数的参数是引用时,我们需要指明参数的传入方式 std::ref(a);
3. 类成员方法的线程化处理
#include <iostream>
#include <thread>
using namespace std;
// 类成员函数线程化方案
class Object
{
public:
Object(int _v):m_val(_v) {}
~Object() {}
void fun(int a)
{
cout << "fun(int a)" << endl;
}
void fun_ra(int& a)
{
cout << "fun_ra(int &a)" << endl;
}
void fun_cra(const int& a)
{
cout << "fun_cra(int &a)" << endl;
}
void fun_ptr(int* p)
{
if (nullptr == p) return;
cout << "fun_ptr(int *p)" << endl;
}
private:
int m_val;
};
int main()
{
int a = 10;
Object obj(a);
std::thread t1(&Object::fun, obj, a);
std::thread t2(&Object::fun_ra, obj, std::ref(a));
std::thread t3(&Object::fun_cra, obj, std::cref(a));
std::thread t4(&Object::fun_ptr, obj, &a);
t1.join(); t2.join(); t3.join(); t4.join();
return 0;
}
3.1 类成员方法在线程化传递参数时,按值传递(形参对象、调用拷贝构造生成对象的副本)
#include <iostream>
#include <thread>
using namespace std;
// 类成员函数线程化方案
class Object
{
public:
Object(int _v):m_val(_v) {}
~Object() {}
void fun(int a)
{
cout << "fun(int a)" << endl;
m_val += 1;
}
void fun_ra(int& a)
{
cout << "fun_ra(int &a)" << endl;
m_val += 1;
}
void fun_cra(const int& a)
{
cout << "fun_cra(int &a)" << endl;
m_val += 1;
}
void fun_ptr(int* p)
{
if (nullptr == p) return;
cout << "fun_ptr(int *p)" << endl;
m_val += 1;
}
int getValue() const { return m_val; }
private:
int m_val;
};
int main()
{
int a = 10;
Object obj(a);
std::thread t1(&Object::fun, obj, a);
std::thread t2(&Object::fun_ra, obj, std::ref(a));
std::thread t3(&Object::fun_cra, obj, std::cref(a));
std::thread t4(&Object::fun_ptr, obj, &a);
t1.join(); t2.join(); t3.join(); t4.join();
cout << obj.getValue() << endl;
// 这里输出的结果是多少?
// 答案是10,因为线程函数调用的时候,传递的obj参数是一个对象的拷贝版本(副本)
// 并不是obj对象本身
return 0;
}
线程函数调用的时候,传递的obj参数是一个对象的拷贝版本(副本),我们可以在成员函数调用时,输出其所在对象的this地址,并且我们可以给出其拷贝构造函数,观察其调用具体过程。
#include <iostream>
#include <thread>
using namespace std;
// 类成员函数线程化方案
class Object
{
public:
Object(int _v):m_val(_v) {}
// 拷贝构造
Object(const Object& rhs)
{
cout << "copy consstruct , this :" << this << endl;
}
~Object() {}
void fun(int a)
{
cout << "fun(int a) , this :" << this << endl;
m_val += 1;
}
void fun_ra(int& a)
{
cout << "fun_ra(int &a) , this :" << this << endl;
m_val += 1;
}
void fun_cra(const int& a)
{
cout << "fun_cra(int &a) , this :" << this << endl;
m_val += 1;
}
void fun_ptr(int* p)
{
if (nullptr == p) return;
cout << "fun_ptr(int *p) , this :"<< this << endl;
m_val += 1;
}
int getValue() const { return m_val; }
private:
int m_val;
};
int main()
{
int a = 10;
Object obj(a);
std::thread t1(&Object::fun, obj, a);
std::thread t2(&Object::fun_ra, obj, std::ref(a));
std::thread t3(&Object::fun_cra, obj, std::cref(a));
std::thread t4(&Object::fun_ptr, obj, &a);
t1.join(); t2.join(); t3.join(); t4.join();
cout << obj.getValue() << endl;
// 这里输出的结果是多少?
// 答案是10,因为线程函数调用的时候,传递的obj参数是一个对象的拷贝版本(副本)
// 并不是obj对象本身
return 0;
}
3.2 类成员方法在线程化传递参数时,按地址传递(线程函数与当前对象关联)
#include <iostream>
#include <thread>
using namespace std;
// 类成员函数线程化方案
class Object
{
public:
Object(int _v) :m_val(_v) {}
// 拷贝构造
Object(const Object& rhs)
{
cout << "copy consstruct , this :" << this << endl;
}
~Object() {}
void fun(int a)
{
cout << "fun(int a) , this :" << this << endl;
m_val += 1;
}
void fun_ra(int& a)
{
cout << "fun_ra(int &a) , this :" << this << endl;
m_val += 1;
}
void fun_cra(const int& a)
{
cout << "fun_cra(int &a) , this :" << this << endl;
m_val += 1;
}
void fun_ptr(int* p)
{
if (nullptr == p) return;
cout << "fun_ptr(int *p) , this :" << this << endl;
m_val += 1;
}
int getValue() const { return m_val; }
private:
int m_val;
};
int main()
{
int a = 10;
Object obj(a);
std::thread t1(&Object::fun, &obj, a);
std::thread t2(&Object::fun_ra, &obj, std::ref(a));
std::thread t3(&Object::fun_cra, &obj, std::cref(a));
std::thread t4(&Object::fun_ptr, &obj, &a);
t1.join(); t2.join(); t3.join(); t4.join();
cout << obj.getValue() << endl;
// 这里输出的结果是多少?
return 0;
}
3.3 类的静态函数线程化
#include <iostream>
#include <thread>
using namespace std;
// 类成员函数线程化方案
class Object
{
public:
Object(int _v) :m_val(_v) {}
~Object() {}
static void show() {cout << "Object::show()" << endl;}
private:
int m_val;
};
int main()
{
int a = 10;
Object obj(a);
std::thread t1(&Object::show);
t1.join();
return 0;
}
3. 仿函数线程化
#include <iostream>
#include <thread>
using namespace std;
// 仿函数
struct Object
{
void operator()()
{
cout << "operator" << endl;
}
void operator()(int a)
{
cout << "operator(int a)" << endl;
}
};
int main()
{
Object obj;
std::thread t1(obj);
std::thread t2(obj,10);
t1.join(); t2.join();
return 0;
}
问题:已知全局函数、类成员函数都可以线程化。那么
内联函数、虚函数是否可以线程化?构造函数、拷贝构造函数是否可以线程化?析构函数…