thread库:c++11引入的多线程跨平台解决方案
thread::id:用于声明一个线程id
this_thread::get_id():获取线程id
thread 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

多线程版本的时钟周期几乎是单线程的一半

最后修改:2021 年 11 月 14 日 07 : 10 PM
如果觉得我的文章对你有用,请随意赞赏