c++11并发语法初步
参考:
https://www.cnblogs.com/haippy/p/3284540.html
https://en.cppreference.com/w/
https://wizardforcel.gitbooks.io/cpp-11-faq/content/77.html
http://www.cplusplus.com/reference/
C++11多线程头文件
C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是
:该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。 :该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。 :该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。 - <condition_variable>:该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
:该头文件主要声明了 std::promise, std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。
多线程的Hello World
#include <bits/stdc++.h>
#include <thread>
using namespace std;
void function_name() {
cout << "hello world" << endl;
}
int main()
{
std::thread t(function_name);
t.join();
return 0;
}
std::thread 构造
- (1). 默认构造函数,创建一个空的 thread 执行对象。
- (2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
- (3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
- (4). move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.
其他函数
get_id | 获取线程ID |
---|---|
joinable | 检查线程是否可以join |
join | join线程 |
detach | detach线程 |
swap | swap线程 |
native_handle | 返回native_handle |
hardware_concurrency[static] | 检查硬件并发性 |
参考:https://en.cppreference.com/w/cpp/thread/thread/thread
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
void f1(int n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 1 executing
";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 2 executing
";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
class foo
{
public:
void bar()
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 3 executing
";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int n = 0;
};
int main()
{
int n = 0;
foo f;
std::thread t1; // t1 is not a thread
std::thread t2(f1, n + 1); // pass by value
std::thread t3(f2, std::ref(n)); // pass by reference
std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f
t2.join();
t4.join();
t5.join();
std::cout << "Final value of n is " << n << '
';
std::cout << "Final value of foo::n is " << f.n << '
';
}
Thread 1 executing
Thread 2 executing
Thread 3 executing
Thread 3 executing
Thread 1 executing
Thread 2 executing
Thread 2 executing
Thread 3 executing
Thread 1 executing
Thread 3 executing
Thread 2 executing
Thread 1 executing
Thread 3 executing
Thread 1 executing
Thread 2 executing
Final value of n is 5
Final value of foo::n is 5
std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f
保留疑惑,函数名即是地址,加&还是地址,但是看别人怎么写。
move (1) | thread& operator= (thread&& rhs) noexcept; |
---|---|
copy [deleted] (2) | thread& operator= (const thread&) = delete; |
- (1). move 赋值操作,如果当前对象不可 joinable,需要传递一个右值引用(rhs)给 move 赋值操作;如果当前对象可被 joinable,则 terminate() 报错。
- (2). 拷贝赋值操作被禁用,thread 对象不可被拷贝。
互斥量
Mutex 系列类(四种)
- std::mutex,最基本的 Mutex 类。
- std::recursive_mutex,递归 Mutex 类。
- std::time_mutex,定时 Mutex 类。
- std::recursive_timed_mutex,定时递归 Mutex 类。
Lock 类(两种)
- std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁。
- std::unique_lock,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。
其他类型
- std::once_flag
- std::adopt_lock_t
- std::defer_lock_t
- std::try_to_lock_t
函数
- std::try_lock,尝试同时对多个互斥量上锁。
- std::lock,可以同时对多个互斥量上锁。
- std::call_once,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。
并发最大的问题就是对临界区资源的访问,主要是通过互斥量上锁的方法解决。
http://www.cplusplus.com/reference/mutex/mutex/try_lock/ 具体细节看库即可,下面解析一些常见的
主要用unique_lock,在构造函数上锁,在析构函数解锁,第一个参数为互斥量,第二个是可选
- std::adopt_lock_t
- std::defer_lock_t
- std::try_to_lock_t
https://en.cppreference.com/w/cpp/thread/lock_tag
#include <bits/stdc++.h>
using namespace std;
int main()
{
std::mutex my_mutex;
std::unique_lock<std::mutex> lock(my_mutex);
///表示这个互斥量已经lock成功了,通知构造函数不用在lock了,
my_mutex.lock(); ///必须先lock才能用
std::unique_lock<std::mutex> lock1(my_mutex, std::adopt_lock);
///后面会自己unlock()
///============================
///尝试锁,没锁成功不阻塞, 前提互斥量不能被锁定
std::unique_lock<std::mutex> lock2(my_mutex, std::try_to_lock);
if(lock.owns_lock()) {
///拿到了锁头
} else {
///没有拿到锁头
}
///============================
///我没加锁,后面手动锁,方便调用函数, 前提互斥量不能被锁定
std::unique_lock<std::mutex> lock3(my_mutex, std::defer_lock);
lock3.lock(); ///加锁
lock3.unlock(); ///不加锁
if(lock3.try_lock() == true) {
///拿到锁头了
} else {
///没拿到
}
///释放互斥量
std::mutex *m = lock3.release();
return 0;
}
std::condition_variable
一个线程等待另一个线程条件满足
void function_name() {
unique_lock<mutex> lock(mutex1);
///能往下走,肯定互斥量默认被锁了,到wait。
///第二个参数可选,没写默认false。true为不堵塞,继续走。
///如果是false卡在这等待其他线程的con.notify_one()的函数
///注意notify_one的时候必须卡在wait的地方,否则唤醒失败
///notify_all()
con.wait(lock, [] () {
if(true) {
return true;
}
return false;
});
}
std::async, std::future 线程返回值
std::async启动一个异步任务后,返回一个std::future访问异步操作结果的机制
#include <bits/stdc++.h>
#include <future>
using namespace std;
int mythread() {
cout << "mythread = " << this_thread::get_id() << endl;
return 5;
}
int main () {
cout << "main = " << this_thread::get_id() << endl;
///压根没创建子线程,在主线程中做,视乎默认是这个
std::future<int>ret = std::async(std::launch::deferred, mythread);
///async 新线程在这里开始
std::future<int>ret = std::async(std::launch::async, mythread);
cout << ret.get(); ///卡在这里等结果
return 0;
}
用packaged_task打包线程,就多了一个可以返回结果的future接口
#include <bits/stdc++.h>
#include <future>
#include <thread>
using namespace std;
int mythread() {
cout << "mythread = " << this_thread::get_id() << endl;
return 5;
}
int main () {
///std::packaged_task 打包可调用对象
std::packaged_task<int()>mypt(mythread);
std::thread t1(std::ref(mypt));
t1.join();
std::future<int>res = mypt.get_future();
return 0;
}
后续就可以这么用
///
vector<std::std::packaged_task<int()>> vec;
///不用移动语义可能会出问题
vec.push_back(std::move(mypt));