std::thread
class thread;
:Class to represent individual threads of execution.
void callback1() {
cout << "hello, callback1" << endl;
}
void callback2(int i) {
cout << "hello, callback2, i = "<< i << endl;
}
int main() {
//空参的回调函数
thread t1(callback1);
//带有一个参数的回调函数
thread t2(callback2, 9527);
t1.join(); //必须回收,否则程序会崩溃
t2.join();
return 0;
}
设置线程分离:
void callback() {
std::cout << "hello,callback" << std::endl;
}
int main() {
std::thread t1(callback);
t1.detach(); //设置线程分离
this_thread::sleep_for(chrono::seconds(1));
return 0;
}
通过std::thread
创建的线程是不可以复制的,但是可以使用std::move()
移动
int main() {
std::thread t1([](){std::cout << "hello, callback" << std::endl;});
std::thread t2(std::move(t1));
t2.join();
return 0;
}
注意在为线程回调传递引用参数时必须用 ref
转化,因为 thread
的构造函数接受的参数是右值引用类型的:
template <class Fn, class... Args>explicit thread (Fn&& fn, Args&&... args);
void foo(int& x) {
x += 1;
}
int main() {
int a = 1;
thread t(foo, ref(a));
t.join();
cout << a << endl;
return 0;
}
关于 ref()
:C++ std::ref 详解
关于 C++ 中的右值和右值引用:
std::this_thread
this_thread
是一个命名空间,其包含如下 4 个工具函数:
thread::id get_id() noexcept;
void yield() noexcept; // Yield to other threads
template <class Rep, class Period>
void sleep_for (const chrono::duration<Rep,Period>& rel_time);
template <class Clock, class Duration>
void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);
chrono
是一个 C++ 的时间库,seconds
是一个类模板的一种实例化(是一个类):
typedef duration < /*see rep below*/ > seconds;
duration
是一个类模板:
template <class Rep, class Period = ratio<1> >class duration;
其中,ratio
是一个表示分数的类模板:
template <intmax_t N, intmax_t D = 1> class ratio;
typedef std::ratio<1,3> one_third; // 1/3
std::cout << "one_third= " << one_third::num << "/" << one_third::den << std::endl;
命名空间 this_thread
提供了 get_id()
函数用以获取线程id:
cout << this_thread::get_id() << endl; //获取当前线程id
cout << t1.get_id() << endl; //获取t1的id
std::mutex
class mutex;
:用于加解锁的互斥类
创建五个子线程,模拟竞争进入临界区的情况:
void callback() {
std::thread::id id = std::this_thread::get_id();
std::cout <<id<<" entrying..." << std::endl;
std::cout <<id<<" doing work" << std::endl;
std::cout <<id<<" leveling..." << std::endl;
}
int main() {
std::vector<std::thread> tv;
for (int i = 0; i < 5; ++i) {
tv.push_back(std::thread(callback));
}
for (int i = 0; i < 5; ++i) {
tv[i].join();
}
return 0;
}
可以看出线程的执行循序杂乱无章:
1484 entrying...
3528 entrying...
3528 doing work
3528 leveling...
1484 doing work
1484 leveling...
10096 entrying...
10096 doing work
9868 entrying...
9868 doing work
9868 leveling...
10096 leveling...
4408 entrying...
4408 doing work
4408 leveling...
使用锁std::mutex
进行线程互斥:
void callback() {
g_lock.lock();
std::thread::id id = std::this_thread::get_id();
std::cout <<id<<" entrying..." << std::endl;
std::cout <<id<<" doing work" << std::endl;
std::cout <<id<<" leveling..." << std::endl;
g_lock.unlock();
}
看起来输出有序了,但是这样做是不安全的,如果在解锁之前函数因为某种原因异常退出没有解锁,会导致后续线程无法再进入临界区
解决方案是采用lock_guard
,利用Resource Acquisition Is Initialization
特性
std::lock_guard
类模板:template <class Mutex> class lock_guard;
下面是一个 std::lock_guard
的模拟实现:
template<class Mutex> class LockGuard {
public:
using mutex_type=Mutex;
//构造时即加锁
explicit LockGuard(Mutex& mtx) : m_mutex(mtx) {
m_mutex.lock();
}
//析构时解锁
~LockGuard() noexcept{
m_mutex.unlock();
}
//禁止拷贝
LockGuard(const LockGuard&) = delete;
//LockGuard& operater=(const LockGuard&) = delete;
private:
Mutex& m_mutex;
};
std::mutex g_lock;
void callback() {
LockGuard<std::mutex> lock(g_lock);
std::thread::id id = std::this_thread::get_id();
std::cout <<id<<" entrying..." << std::endl;
std::cout <<id<<" doing work" << std::endl;
std::cout <<id<<" leveling..." << std::endl;
//离开作用域,lock被析构,自动解锁
}
当然,lock_gurad
是一个提供好的类,直接调用即可无需自己实现:
std::lock_guard<std::mutex> lock(g_mutex); //加锁
std::atomic
类模板:Objects of atomic types contain a value of a particular type (T)
用于定义一个原子化(线程安全)的类型。例如下面的代码原本的 int n = 0
是不安全的,改为 atomic<int> n = 0
后线程安全:
// int n = 0;
atomic<int> n = 0;
void count10000() {
for (int i = 0; i < 10000; i++)
n++;
}
int main() {
thread ths[100];
for(thread& t : ths) {
t = thread(count10000);
}
for(thread& t : ths) {
t.join();
}
cout << n << endl;
return 0;
}
std::async
函数模板:
template <class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type>
async(launch policy, Fn&& fn, Args&&... args);
int adder(int x, int y) {
this_thread::sleep_for(chrono::seconds(1));
return x + y;
}
int main() {
int a{10}, b{20};
auto f = async(launch::async, adder, a, b);
cout << f.get() << endl;
return 0;
}
async
是一个函数模板,它的第一个参数是启动策略,它控制 std::async 的异步行为,我们可以用三种不同的启动策略来创建std::async
- std::launch::async:保证异步行为,即传递函数将在单独的线程中执行
- std::launch::deferred:当其他线程调用get()来访问共享状态时,将调用非异步行为
- std::launch::async | std::launch::deferred:默认行为。有了这个启动策略,它可以异步运行或不运行,这取决于系统的负载,但我们无法控制它
第二个参数传入一个可调用对象,后面传入可调用对象的参数,跟 thread()
类似
async()
的返回类型是 future
,可以通过它的 get()
方法阻塞等待线程的执行结果
std::future
类模板:
template <class T> future;
template <class R&> future<R&>;
bool is_prime (int x) {
for (int i=2; i<x; ++i) if (x%i==0) return false;
return true;
}
int main () {
// call function asynchronously:
std::future<bool> fut = std::async(is_prime, 444444443);
// do something while waiting for function to set future:
std::cout << "checking, please wait;";
std::chrono::milliseconds span (100);
std::string s = "|/-\\";
int i = 0;
while (fut.wait_for(span) == std::future_status::timeout)
std::cout << "\b" << s[(i++) % 4] << std::flush;
bool x = fut.get(); // retrieve return value
std::cout << "\n444444443" << (x?"is":"is not") << " prime\n";
return 0;
}
wait_for()
: Wait for ready during time span
template <class Rep, class Period>
future_status wait_for(const chrono::duration<Rep,Period>& rel_time) const;
可能的返回值有如下 3 种:
timeout
表示等待指定时长后任务尚未完成
参考文章: