C++多线程快速入门(一):基本&常用操作

case1:创建线程1 join、detach

创建线程,并等该线程执行完毕,并且打印两个线程的id

#include <iostream>
#include <thread>
using namespace std;
void func() {
    
    
    cout << "hello , this is my thread, thread id is " << this_thread::get_id() << endl;
}
int main() {
    
    
    thread th = thread(func);
    th.join();
    cout << "this is main thread and its id is " << this_thread::get_id() << endl;
}

执行结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
hello , this is my thread, thread id is 2
this is main thread and its id is 1

Process finished with exit code 0

使用detach,放弃对该线程的控制:

#include <iostream>
#include <thread>

using namespace std;

void func() {
    
    
    cout << "hello , this is my thread, thread id is " << this_thread::get_id() << endl;
}
int main() {
    
    
    thread th = thread(func);
    th.detach();
    // 如果我们此时不再关系该线程的运行情况的话可以使用detach
    cout << th.joinable() << endl;
    cout << "this is main thread and its id is " << this_thread::get_id() << endl;
}

运行结果:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
0
this is main thread and its id is 1
hello , this is my thread, thread id is 2

Process finished with exit code 0

case2:创建线程2 线程传参 传值或者传引用

传值

#include <iostream>
#include <thread>

using namespace std;

void func(string s) {
    
    
    cout << "hello , this is my thread, thread arg is " << s << endl;
}
int main() {
    
    
    thread th = thread(func, "test");
    th.join();
    cout << "this is main thread and its id is " << this_thread::get_id() << endl;
}

打印结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
hello , this is my thread, thread arg is test
this is main thread and its id is 1

Process finished with exit code 0

传引用

#include <iostream>
#include <thread>

using namespace std;

void func(string& s) {
    
    
    cout << (&s) << endl;
    cout << "hello , this is my thread, thread arg is " << s << endl;
}
int main() {
    
    
    string str = "test";
    thread th = thread(func, ref(str));
    cout << (&str) << endl;
    th.join();
    cout << "this is main thread and its id is " << this_thread::get_id() << endl;
}

打印结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
0x62fd10
0x62fd10
hello , this is my thread, thread arg is test
this is main thread and its id is 1

Process finished with exit code 0

case3:创建线程 线程传参 functional object作为参数

关于仿函数的定义可以看:仿函数
如果单纯地将逻辑函数传入thread,在函数逻辑比较复杂的时候不太好。
将函数封装到类的内部,可以赋予一定的封装性,并且可以在类内部创建例如map的数据结构来记录函数运行的状态。
仿函数、不带参

#include <iostream>
#include <thread>

using namespace std;

// functional object
struct A {
    
    
    void operator()() {
    
    
        cout << "I'm A" << endl;
    }
};
void show() {
    
    
    cout << "I'm show" << endl;
}
int main() {
    
    
    show();
    A a;
    a();    // 等价于 a.operator()();
    // 我们把这种object称为callable的object ,又称为仿函数
    thread thread1 = thread(A());
    thread1.join();
    return 0;
}

打印结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
I'm show
I'm A
I'm A

Process finished with exit code 0

仿函数、带参

#include <iostream>
#include <thread>

using namespace std;

// functional object
struct A {
    
    
    void operator()(int num) {
    
    
        for (int i = 0; i < num; i++) {
    
    
            cout << "I'm A" << i << endl;
        }
    }
};

int main() {
    
    
    int num = 10;
    thread thread1 = thread(A(), num);
    for (int i = 0; i < num; i++) {
    
    
        cout << "I'm main" << i << endl;
    }
    thread1.join();
    return 0;
}

打印结果如下,是乱序的,符合多线程特性
并且 cout 并不是线程安全的,所以可以发现,有乱码的样子

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
I'm mainI'm A00

I'm A1
I'm A2
I'm A3
I'm AI'm main41

I'm mainI'm A25

I'm mainI'm A36

I'm AI'm main47

I'm main5
I'm mainI'm A86

I'm mainI'm A97

I'm main8
I'm main9

Process finished with exit code 0

lambda函数作为参数

#include <iostream>
#include <thread>

using namespace std;

int main() {
    
    
    string s = "test";
    thread f = thread([&s](int a,int b) {
    
    
        cout << s << endl;
        cout << a + b << endl;
    }, 2, 3);
    f.join();
    return 0;
}

打印结果:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
test
5

Process finished with exit code 0

case4:观察多线程程序加速计算

#include <iostream>
#include <thread>

using namespace std;

// 测量一个函数的运行时间
template <class T>
void measure(T&& func) {
    
    
    using namespace std::chrono;
    auto start = system_clock::now();
    // func
    func();
    duration<double> diff = system_clock::now() - start;
    cout << "执行了" << diff.count() << "秒" << endl;
}

// 求和函数[start,end)
void sum(long start, long end, long& ans) {
    
    
    long s = 0;
    for(auto i = start; i < end; i++) {
    
    
        s += i;
    }
    ans = s;
}

const long S = 100000000;

int main() {
    
    
    // 测量一下把工作分摊给两个线程做的时间
    measure([](){
    
    
        long ans1, ans2;
        thread t1 = thread(sum, 0, S >> 1, std::ref(ans1));
        thread t2 = thread(sum, S >> 1, S, std::ref(ans2));
        t1.join();
        t2.join();
        cout << "ans = " << ans1 + ans2 << endl;
    });
    // 测量一下单线程工作时间
    measure([](){
    
    
        long ans;
        sum(0, S, ans);
        cout << "ans = " << ans << endl;
    });
    return 0;
}

打印结果:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
ans = 887459712
执行了0.13546秒
ans = 887459712
执行了0.240006秒

Process finished with exit code 0

当然这里有一个细节:
如果在多线程程序中我们传入了一个ref引用,在线程程序中我们最好不要直接对这个ref值操作,而是采用一个临时变量,例如上面的程序中我们就使用了一个临时变量s,对s进行累加后再把值赋给ans。这样会提高程序运行效率。

case5:future + get 获取并发结果

future包裹线程运行结果,使用方法如下:
由于我们线程函数的结果是long,所以首先定义future<long>

#include <iostream>
#include <thread>
#include <future>
#include <vector>

using namespace std;

// 测量一个函数的运行时间
template <class T>
void measure(T&& func) {
    
    
    using namespace std::chrono;
    auto start = system_clock::now();
    // func
    func();
    duration<double> diff = system_clock::now() - start;
    cout << "执行了" << diff.count() << "秒" << endl;
}

// 求和函数[start,end)
long sum(long start, long end) {
    
    
    long s = 0;
    for(auto i = start; i < end; i++) {
    
    
        s += i;
    }
    return s;
}

const long S = 100000000;

int main() {
    
    
    // 测量一下把工作分摊给 threadNums 个线程做的时间
    measure([](){
    
    
       const long threadNums = 8;
       vector<future<long>> vec;
       vec.reserve(threadNums);
       for (int i = 0; i < threadNums; i++) {
    
    
           vec.push_back(async(sum, (S / threadNums) * i, (S / threadNums) * (i + 1)));
       }
       long ans = 0;
       // get 阻塞式地拿到并发结果
       for (int i = 0; i < threadNums; i++) {
    
    
           ans += vec[i].get();
       }
       cout << "ans = " << ans << endl;
    });
    // 测量一下单线程工作时间
    measure([](){
    
    
        long ans = sum(0, S);
        cout << "ans = " << ans << endl;
    });
    return 0;
}

运行结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
ans = 887459712
执行了0.0521455秒
ans = 887459712
执行了0.250044秒

Process finished with exit code 0

case6:互斥锁

多个线程访问同一个值并且对其修改,会导致数据竞争。:

#include <iostream>
#include <thread>
#include <future>
#include <vector>

using namespace std;
// 测量一个函数的运行时间
template <class T>
void measure(T&& func) {
    
    
    using namespace std::chrono;
    auto start = system_clock::now();
    // func
    func();
    duration<double> diff = system_clock::now() - start;
    cout << "执行了" << diff.count() << "秒" << endl;
}

std::mutex mtx;
// 求和函数 线程安全的
void sum(long& s) {
    
    
    mtx.lock();
    for (int i = 0; i < 100000; i++) {
    
    
        s++;
    }
    mtx.unlock();
}

int main() {
    
    
    // 测量一下把工作分摊给 threadNums 个线程做的时间
    measure([](){
    
    
        vector<thread> v;
        long s = 0;
        for (int i = 0; i < 4; i++) {
    
    
             v.emplace_back(std::thread(sum, std::ref(s)));
         }
        for (int i = 0; i < 4; i++) {
    
    
            v[i].join();
        }
        cout << "ans " << s << endl;
    });
    measure([](){
    
    
        long s = 0;
        for (int i = 0; i < 4; i++) {
    
    
            sum(s);
        }
        cout << "ans " << s << endl;
    });
    return 0;
}

测试结果:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
ans 400000
执行了0.0141654秒
ans 400000
执行了0.0155926秒

Process finished with exit code 0

case7:std::lock_guard 类模板

如果mtx.lock()和mtx.unlock()之间的语句发生了异常,unlock()语句没有机会执行!
这会导致mtx一直处于锁着的状态,其他使用sum函数的线程就会阻塞。
c++库已经提供了std::lock_guard类模板,构造时自动加锁,析构时自动解锁:
将case6的sum函数修改为下面形式即可:

// 求和函数 线程安全的
void sum(long& s) {
    
    
    std:;lock_guard<std::mutex> guard(mtx);
    // mtx.lock();
    for (int i = 0; i < 100000; i++) {
    
    
        s++;
    }
    // mtx.unlock();
}

如果想自己实现的lock_guard的话也可以:

class mutexLockGuard{
    
    
private:
    std::mutex& mtx;
public:
    explicit mutexLockGuard(std::mutex& mutex) : mtx(mutex){
    
    
        mtx.lock();
    }
    ~mutexLockGuard() {
    
    
        mtx.unlock();
    }
};

笔记参考:
C++多线程快速入门

Guess you like

Origin blog.csdn.net/qq_42604176/article/details/120874665