C++11 多线程(std::thread)实例

发布时间 2023-11-24 16:51:54作者: 量子与太极

C++11的std::thread
在C中已经有一个叫做pthread的东西来进行多线程编程,但是并不好用 (如果你认为句柄、回调式编程很实用,那请当我没说),所以c++11标准库中出现了一个叫作std::thread的东西。

std::thread常用成员函数
构造&析构函数

举个栗子

例一:thread的基本使用

 1 // Compiler: MSVC 19.29.30038.1
 2 // C++ Standard: C++17
 3 #include <iostream>
 4 #include <thread>
 5 using namespace std;
 6 void doit() { cout << "World!" << endl; }
 7 int main() {
 8     // 这里的线程a使用了 C++11标准新增的lambda函数
 9     // 有关lambda的语法,请参考我之前的一篇博客
10     // https://blog.csdn.net/sjc_0910/article/details/109230162
11     thread a([]{
12         cout << "Hello, " << flush;
13     }), b(doit);
14     a.join();
15     b.join();
16     return 0;
17 }

那么,为什么会有不同的结果呢?
这就是多线程的特色!

多线程运行时是以异步方式执行的,与我们平时写的同步方式不同。异步方式可以同时执行多条语句。

在上面的例子中,我们定义了2个thread,这2个thread在执行时并不会按照一定的顺序。打个比方,2个thread执行时,就好比赛跑,谁先跑到终点,谁就先执行完毕。

例二:thread执行有参数的函数
 1 // Compiler: MSVC 19.29.30038.1
 2 // C++ Standard: C++17
 3 #include <iostream>
 4 #include <thread>
 5 using namespace std;
 6 void countnumber(int id, unsigned int n) {
 7     for (unsigned int i = 1; i <= n; i++);
 8     cout << "Thread " << id << " finished!" << endl;
 9 }
10 int main() {
11     thread th[10];
12     for (int i = 0; i < 10; i++)
13         th[i] = thread(countnumber, i, 100000000);
14     for (int i = 0; i < 10; i++)
15         th[i].join();
16     return 0;
17 }

 

注意:我说的是有可能。你的运行结果可能和我的不一样,这是正常现象,在上一个例子中我们分析过原因。

这个例子中我们在创建线程时向函数传递了一些参数,但如果要传递引用参数呢?是不是像这个例子中直接传递就行了?

让我们来看看第三个例子:

例三:thread执行带有引用参数的函数
 1 // Compiler: MSVC 19.29.30038.1
 2 // C++ Standard: C++17
 3 #include <iostream>
 4 #include <thread>
 5 using namespace std;
 6 template<class T> void changevalue(T &x, T val) {
 7     x = val;
 8 }
 9 int main() {
10     thread th[100];
11     int nums[100];
12     for (int i = 0; i < 100; i++)
13         th[i] = thread(changevalue<int>, nums[i], i+1);
14     for (int i = 0; i < 100; i++) {
15         th[i].join();
16         cout << nums[i] << endl;
17     }
18     return 0;
19 }

如果你尝试编译这个程序,那你的编译器一定会报错

 这是怎么回事呢?原来thread在传递参数时,是以右值传递的:

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
using namespace std;
template<class T> void changevalue(T &x, T val) {
    x = val;
}
int main() {
    thread th[100];
    int nums[100];
    for (int i = 0; i < 100; i++)
        th[i] = thread(changevalue<int>, ref(nums[i]), i+1);
    for (int i = 0; i < 100; i++) {
        th[i].join();
        cout << nums[i] << endl;
    }
    return 0;
}

这次编译可以成功通过,你的程序输出的结果应该是这样的:

 1 // Compiler: MSVC 19.29.30038.1
 2 // C++ Standard: C++17
 3 #include <iostream>
 4 #include <thread>
 5 using namespace std;
 6 int n = 0;
 7 void count10000() {
 8     for (int i = 1; i <= 10000; i++)
 9         n++;
10 }
11 int main() {
12     thread th[100];
13     // 这里偷了一下懒,用了c++11的foreach结构
14     for (thread &x : th)
15         x = thread(count10000);
16     for (thread &x : th)
17         x.join();
18     cout << n << endl;
19     return 0;
20 }

例四:std::mutex的使用

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int n = 0;
mutex mtx;
void count10000() {
    for (int i = 1; i <= 10000; i++) {
        mtx.lock();
        n++;
        mtx.unlock();
    }
}
int main() {
    thread th[100];
    for (thread &x : th)
        x = thread(count10000);
    for (thread &x : th)
        x.join();
    cout << n << endl;
    return 0;
}

 1 // Compiler: MSVC 19.29.30038.1
 2 // C++ Standard: C++17
 3 #include <iostream>
 4 #include <thread>
 5 // #include <mutex> //这个例子不需要mutex了
 6 #include <atomic>
 7 using namespace std;
 8 atomic_int n = 0;
 9 void count10000() {
10     for (int i = 1; i <= 10000; i++) {
11         n++;
12     }
13 }
14 int main() {
15     thread th[100];
16     for (thread &x : th)
17         x = thread(count10000);
18     for (thread &x : th)
19         x.join();
20     cout << n << endl;
21     return 0;
22 }

 

 1 // Compiler: MSVC 19.29.30038.1
 2 // C++ Standard: C++17
 3 #include <iostream>
 4 #include <thread>
 5 #include <future>
 6 using namespace std;
 7 int main() {
 8     async(launch::async, [](const char *message){
 9         cout << message << flush;
10     }, "Hello, ");
11     cout << "World!" << endl;
12     return 0;
13 }

 1 // Compiler: MSVC 19.29.30038.1
 2 // C++ Standard: C++17
 3 #include <iostream>
 4 // #include <thread> // 这里我们用async创建线程
 5 #include <future> // std::async std::future
 6 using namespace std;
 7 
 8 template<class ... Args> decltype(auto) sum(Args&&... args) {
 9     // C++17折叠表达式
10     // "0 +"避免空参数包错误
11     return (0 + ... + args);
12 }
13 
14 int main() {
15     // 注:这里不能只写函数名sum,必须带模板参数
16     future<int> val = async(launch::async, sum<int, int, int>, 1, 10, 100);
17     // future::get() 阻塞等待线程结束并获得返回值
18     cout << val.get() << endl;
19     return 0;
20 }