C++11线程相关
1.Thread
thread库底层是对各个系统的线程库进⾏封装,如Linux下的pthread库和Windows下Thread库等,所以C++11 thread库的第⼀个特点是可以跨平台,第⼆个特点是Linux和Windows下提供的线程库都是⾯向过程的,C++11 thread是库⾯向对象的,并且融合了⼀些C++11语⾔特点,如右值引⽤的移动语义,可变模板参数等,⽤起来会更好⽤⼀些。
构造:
常用接口介绍:
void join(); //用于主线程等待子线程的结束,并回收对应的线程资源,主线程会阻塞住。
bool joinable(); //用于判断子线程是否执行完成,不会阻塞,直接返回。
void detach(); //将子线程交给操作系统回收,不需要主线程手动回收。
1.2this_thread
this_thread是一个命名空间,里面有对应的接口获取当前线程的信息和控制当前线程。
接口介绍:
①thread::id get_id(); //获取当前线程的id
②void yield(); //让出执行权,到对应的队尾排队
template <class Clock, class Duration>
③ void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);//让线程休眠(绝对时间)
template <class Rep, class Period>
④void sleep_for (const chrono::duration<Rep,Period>& rel_time);//让线程休眠(相对时间)
2.mutex
mutex是封装的互斥锁的类,⽤于保护临界区的共享数据。mutex主要提供lock和unlock两个接⼝
函数。 mutex 提供排他性⾮递归所有权语义:(1)调⽤⽅线程从它成功调⽤ lock或 try_lock 开
始,到它调⽤ unlock 为⽌占有 mutex。(2)线程占有 mutex 时,其他线程如果试图要求 mutex
的所有权,那么就会阻塞(对于 lock 的调⽤), 对于 try_lock就会返回false 。
2.1传参时的细节
mutex是不支持拷贝的,在传参时要注意。
例①:将锁传给对应的线程,需要加上ref()
void Print(int n, int& rx, mutex& rmtx)
{rmtx.lock();for (int i = 0; i < n; i++){++rx;}rmtx.unlock();
}int main()
{int x = 0;mutex mtx;// 这⾥必须要⽤ref()传参,现成中拿到的才是x和mtx的引⽤,// 具体原因需要看下⾯thread源码中的分析// https://legacy.cplusplus.com/reference/functional/ref/?kw=refthread t1(Print, 1000000, ref(x), ref(mtx));thread t2(Print, 2000000, ref(x), ref(mtx));t1.join();t2.join();cout << x << endl;return 0;
}
例②:直接使用lambda,绕开ref()
int main()
{int x = 0;mutex mtx;
// 将上⾯的代码改成使⽤lambda捕获外层的对象,也就可以不⽤传参数,间接解决了上⾯的问题auto Print = [&x, &mtx](size_t n) {mtx.lock();for (size_t i = 0; i < n; i++){++x;}mtx.unlock();};thread t1(Print, 1000000);thread t2(Print, 2000000);t1.join();t2.join();cout << x << endl;return 0;
}
3.时间锁和递归锁
4.lock_guard
lock_guard是C++11提供的⽀持RAII⽅式管理互斥锁资源的类,这样可以更有效的防⽌因为异常等原因导致的死锁问题。lock_guard的功能简单纯粹,仅仅⽀持RAII的⽅式管理锁对象。也可以在构造的时候通过传参adopt_lock_t的adopt_lock对象管理已经lock的锁对象。其次lock_guard类不⽀持拷⻉构造lock_guard (mutex_type& m, adopt_lock_t tag); //管理已经上锁的锁
示例:
int main()
{int x = 0;mutex mtx;auto Print = [&x, &mtx](size_t n) {lock_guard<mutex> lock(mtx); //自动上锁和释放锁for (size_t i = 0; i < n; i++){++x;}};thread t1(Print, 1000000);thread t2(Print, 2000000);t1.join();t2.join();return 0;
}
5.unique_lock
unique_lock也是C++11提供的⽀持RAII⽅式管理互斥锁资源的类,相⽐lock_guard他的功能⽀持
更丰富复杂并且支持手动释放锁。
6.lock和try_lock
lock:是⼀个函数模板,可以⽀持对多个锁对象同时锁定,如果其中⼀个锁对象没有锁住,lock函数会把已经锁定的对象解锁⽽进⼊阻塞,直到锁定所有的所有的对象。template <class Mutex1, class Mutex2, class... Mutexes>
void lock (Mutex1& a, Mutex2& b, Mutexes&... cde);
try_lock:也是⼀个函数模板,尝试对多个锁对象进⾏同时尝试锁定,如果全部锁对象都锁定了,返回-1,如果某⼀个锁对象尝试锁定失败,把已经锁定成功的锁对象解锁,并则返回这个对象的下标(第⼀个参数对象,下标从0开始算). 不会阻塞template <class Mutex1, class Mutex2, class... Mutexes>
int try_lock (Mutex1& a, Mutex2& b, Mutexes&... cde);
lock()使用示例:
std::mutex foo, bar;
void task_a()
{std::lock(foo, bar); std::cout << "task a\n";foo.unlock();bar. Unlock();
}
void task_b()
{std::lock(bar, foo);std::cout << "task b\n";bar.unlock();foo. Unlock();
}
try_lock()使用示例:
std::mutex foo, bar;
void task_a()
{foo. Lock();std::cout << "task a\n";bar.lock();// ..bar. Unlockck();bar.unlock();
}
void task_b()
{int x = try_lock(bar, foo);if (x == -1) {std::cout << "task b\n";// ...bar.unlock();foo.unlock();}else std::cout << "[task b failed: mutex " << (x ? "foo" : "bar")<< " locked]\n";
}
7.call_once
多线程执⾏时,让第⼀个线程执⾏Fn⼀次,其他线程不再执⾏Fn。
template <class Fn, class... Args>
void call_once (once_flag& flag, Fn&& fn, Args&&... args);
示例: