thread库
:c++11引入的多线程跨平台解决方案thread::id
:用于声明一个线程idthis_thread::get_id()
:获取线程idthread thd(func)
:开启线程的方式,参数为一个回调函数或者lambda表达式thread.join()
:主线程会等待其他线程结束后再停止thread.detach()
: 主线程不必等待其他线程结束thread::handware_concurrency()
:正常返回支持的并发线程数,若值非良定义或不可计算,则返回 0。此值作为参考即可chrono库
:chrono库拥有一些时间的函数
1 多线程初始化方式
#include <iostream>
#include <thread>
using namespace std;
void printAll(int a, int b, int c) {
cout << a << " " << b << " " << c << endl;
}
void add(int a, int b, int& c) {
c = a + b;
}
void printString(const string& info, const string& info2) {
cout << info << " " << info2 << endl;
}
void testThreadInit() {
int a = 3;
int b = 4;
int c = 5;
thread thread1([=] {printAll(a, b, c);}); //初始化方式一
thread1.join();
thread thread2(printAll, a, b, c); //初始化方式二
thread2.join();
thread thread3([=, &c] {add(a, b, c);});
thread3.join();
thread thread4(add, a, b, ref(c)); //利用ref相当于引用传递
thread4.join();
string abc("abc");
string def("def");
thread thread5([&]{printString(abc, def);}); //推荐这种方式
thread5.join();
thread thread6(printString, abc, def); //这种调用方式效率低
thread6.join();
thread thread7(printString, cref(abc), cref(def));
thread7.join();
}
int main() {
testThreadInit();
}
2 多线程计算
#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
using namespace std;
double caculate(double v) {
if (v <= 5) {
return v;
}
this_thread::sleep_for(chrono::milliseconds(10)); //暂停100ms
return sqrt((v * v + sqrt((v - 5) * (v + 2.5)) / 2.0) / v);
}
template <typename Iter, typename Fun>
double visitRange(auto id, Iter iterBegin, Iter iterEnd, Fun func) {
if (id == this_thread::get_id()) {
cout << "main thread " << id << endl;
} else {
cout << "another thread " << this_thread::get_id() << endl;
}
double v = 0;
for (auto iter = iterBegin; iter != iterEnd; iter++) {
v += func(*iter);
}
return v;
}
void fillData(vector<double>& v) {
for (int i = 0; i < 1000; i++) {
v.push_back(rand());
}
}
void singleVersion(vector<double>& v) {
double value = 0.0;
auto nowClock = clock();
for (const auto& info : v) {
value += caculate(info);
}
auto finishClock = clock();
cout << value <<" time used: " << finishClock - nowClock << endl;
}
void multiVersion(vector<double>& v, thread::id id) {
auto iter = v.begin() + (v.size() / 2);
double anotherv = 0.0;
auto iterEnd = v.end();
auto nowClock =clock();
auto mainThreadId = id;
thread s([&anotherv, iter, iterEnd, mainThreadId]() {
anotherv = visitRange(mainThreadId, iter, iterEnd, caculate);
});
auto anotherId = this_thread::get_id();//分线程中的id
auto halfv = visitRange(mainThreadId, v.begin(), iter, caculate);
s.join();
auto finishClock = clock();
cout << halfv + anotherv << " time used:" << finishClock - nowClock << endl;
}
int main() {
thread::id mainThreadId = this_thread::get_id(); //获取主线程ID
vector<double> v;
fillData(v);
singleVersion(v);
multiVersion(v, mainThreadId);
}
3 关于共享变量问题的解决
#include <iostream>
#include <atomic>
#include <vector>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std;
class Counter {
public:
Counter() : m_count(0), m_totalResource(0) {
}
void addCount() {
m_count++; // 写入寄存器 寄存器加1 写入内存
}
int count() const {
return m_count;
}
void addResource(int r) {
m_totalResource++;
}
int aveResource() {
if (m_count == 0) {
return 1;
}
return m_totalResource / m_count;
}
void lockMutex() {
m_mutex.lock();
}
void unlockMutex() {
m_mutex.unlock();
}
private:
atomic<int> m_count;
atomic<int> m_totalResource;
mutex m_mutex; //灵活但容易出错,例如加锁不解锁、连加锁两次,把接口交给别人,别人可能会很痛苦
};
bool printStep(Counter &c, int maxCount) {
auto count = c.count();
c.lockMutex();
auto ave = c.aveResource();
if (ave != 1) {
cout << "has bad thing happend \n";
}
c.unlockMutex();
if (count == maxCount) {
cout << "Ok finished \n";
return true;
}
return false;
}
int work(int a) {
return a + a;
}
template<class Iter>
void realWork(Counter& c, atomic<double>& totalValue, Iter b, Iter e) {
for (; b != e; ++b) {
totalValue += work(*b);
c.lockMutex();
c.addCount();//两个add函数间不是原子的
c.addResource(1);
c.unlockMutex();
}
}
void createData(vector<double>& v) {
for (int i = 0; i < 10000000; i++) {
v.push_back(rand());
}
}
void print(double value) {
cout << "total is " << value << endl;
}
int main() {
vector<double> v;
createData(v);
Counter counter;
atomic<double> totalValue;
//singleThread
realWork(counter, totalValue, v.begin(), v.end());
print(totalValue);
//multiThread
Counter counter2;
atomic<double> totalValue2 = 0;
auto iter1 = v.begin() + v.size() / 3;
auto iter2 = v.begin() + v.size() / 3 * 2;
thread monitor([&] {while (!printStep(counter2, 10000000)) {} });
thread thread1([&] {
realWork(counter2, totalValue2, v.begin(), iter1);
});
thread thread2([&] {
realWork(counter2, totalValue2, iter1, iter2);
});
thread1.join();
thread2.join();
realWork(counter2, totalValue2, iter2, v.end());
monitor.join();
cout << "call times: " << counter2.count() << endl;
print(totalValue2);
}
/*
* totalValue2可以换成 用三个变量分别存储的形式,进而再汇总,不推荐直接使用共享变量
*/
4 线程池
#include <iostream>
#include <atomic>
#include <vector>
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
using namespace std;
mutex mut;
atomic<bool> ready{false};
void worker(int i) {
while (!ready) {
this_thread::yield(); //会释放掉cpu,但是别的线程还是会抢占
this_thread::sleep_for(chrono::seconds(1));//这种方法会比较显著地降低cpu利用率
}
// lock_guard<mutex> lock(mut);
// cout << "hello world" << i << "\n";
printf("hello world%d\n", i); //printf是线程安全的
}
int main() {
const auto threadCount = 4;
vector<thread> pool;
for (int i = 0; i < threadCount; ++i) {
pool.emplace_back(worker, i);
}
this_thread::sleep_for(chrono::seconds(20));
ready = true;
for (auto &v : pool) {
if (v.joinable()) {
v.join();
}
}
cout << "byebye " << endl;
}
5 多线程打印1-1000
#include <iostream>
#include <atomic>
#include <vector>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std;
atomic<int> num = 0;
mutex mut;
void print() {
if (num >= 100 ) {
return;
}
lock_guard<mutex> m(mut);
while (num < 100) {
num++;
cout << num << endl;
}
}
int main() {
thread s1(print);
thread s2(print);
print();
s1.join();
s2.join();
}
一些有趣的例子
一个日志类,稍加修改会呈现不同的结果。
version1
#include <iostream>
#include <fstream>
using namespace std;
class Log {
public:
Log(int id, const string& filename) : m_id(id) {
cout << "create " << m_id << endl;
m_f.open(filename.c_str(), fstream::out);
}
~Log() {
cout << "bye " << m_id << endl;
}
void log(const string& info) {
cout << info;
m_f << info;
m_f.flush();
}
private:
int m_id;
fstream m_f;
};
Log log(0, "log.log");
void test() {
// static Log log(1, "log.log");
log.log("hellott");
}
void test2() {
// static Log log(2, "log.log");
log.log("world");
}
int main() {
test();
test2();
}
//正常输出hello world
version2
#include <iostream>
#include <fstream>
using namespace std;
class Log {
public:
Log(int id, const string& filename) : m_id(id) {
cout << "create " << m_id << endl;
m_f.open(filename.c_str(), fstream::out);
}
~Log() {
cout << "bye " << m_id << endl;
}
void log(const string& info) {
cout << info;
m_f << info;
//m_f.flush();
}
private:
int m_id;
fstream m_f;
};
void test() {
static Log log(1, "log.log");
log.log("hellott");
}
void test2() {
static Log log(2, "log.log");
log.log("world");
}
int main() {
test();
test2();
}
//输出hello ,因为先调用test2的析构,此时会将缓冲区数据写入,而后再调用test1的析构,将缓冲区写入,导致world被hello //覆盖
version3
#include <iostream>
#include <fstream>
using namespace std;
class Log {
public:
Log(int id, const string& filename) : m_id(id) {
cout << "create " << m_id << endl;
m_f.open(filename.c_str(), fstream::out);
}
~Log() {
cout << "bye " << m_id << endl;
}
void log(const string& info) {
cout << info;
m_f << info;
m_f.flush();
}
private:
int m_id;
fstream m_f;
};
void test() {
static Log log(1, "log.log");
log.log("hellott");
}
void test2() {
static Log log(2, "log.log");
log.log("world");
}
int main() {
test();
test2();
}
//调用flush,先刷新,所以文件保存的是world
版权属于:superzhaoyang
本文链接:https://www.superzhaoyang.top/archives/52/
转载时须注明出处及本声明