构造std::thread对象时,如果不带参则会创建一个空的thread对象,但底层线程并没有真正被创建,一般可将其他std::thread对象通过move移入其中;如
- 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
如果带参则会创建新线程,而且会被立即运行。包括函数指针、函数对象、lambda等,能让使用者对其进行函数调用操作
如 :explicit thread(_Fn&& _Fx, _Args&&... _Ax)。
- std::thread thread1(funct1,this);
-
- class Func
- {
- public:
- void operator() () const
- {
- do_something();
- }
- };
-
- std::thread myThread((Func()));
- std::thread myThread([]{
- do_something();
- });
std::thread对象不能被复制和赋值,只能被移动。
std::thread对象析构时,会先判断是否可joinable(),如果可联结,则程序会直接被终止出错。这意味着创建thread对象以后,要在随后的某个地方调用join或detach以便让std::thread处于不可联结状态。
- std::thread t2(f1, n + 1); // pass by value
- t2.join();
joinable():用于判断std::thread对象联结状态,一个std::thread对象只可能处于可联结或不可联结两种状态之一。
a. 可联结:当线程己运行或可运行、或处于阻塞时是可联结的。注意,如果某个底层线程已经执行完任务,但是没有被join的话,仍然处于joinable状态。
即std::thread对象(对象由父线程所有)与底层线程保持着关联时,为joinable状态。
b. 不可联结:
① 当不带参构造的std::thread对象为不可联结,因为底层线程还没创建。
② 己移动的std::thread对象为不可联结。
③ 己调用join或detach的对象为不可联结状态。因为调用join()以后,底层线程己结束,而detach()会把std::thread对象和对应的底层线程之间的连接断开。
join():等待子线程,调用线程处于阻塞模式。join()执行完成之后,底层线程id被设置为0,即joinable()变为false。join() 仅能调用一次;只要 std::thread 对象曾经调用过 join(),线程就不再可汇合(joinable)
detach():分离子线程,与当前线程的连接被断开,子线程成为后台线程,被C++运行时库接管。则 *this 不再代表任何的线程执行实例。 joinable() = false get_id() = std::thread::id()
用这函数,主要传给子线程的参数不能是当前线程的局部变量,不然生命周期结束了,还在子线程使用,会崩溃。
get_id:获取线程ID,返回一个类型为std::thread::id的对象。
swap:交换两个线程对象所代表的底层句柄。
std::this_thread::sleep_until: 线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。
std::this_thread::sleep_for: 线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比 sleep_duration 所表示的时间片更长
- std::chrono::milliseconds dura( 2000 );
- std::this_thread::sleep_for( dura );
lock、unlock
std::lock_guard RAII(资源获取即初始化)。
std::mutex mt;
std::lock_guard
RAII(资源获取即初始化)。std::unique_lock 占用更多的空间,也比 std::lock_guard 略慢。但 std::unique_lock 对象可以不占有关联的互斥,具备这份灵活性需要付出代价:需要存储并且更新互斥信息。
std::mutex mt;
std::unique_lock
std::condition_variable condit;
wait函数
1.只有独占锁一个参数变量,void wait( std::unique_lockstd::mutex& lock );
wait()将解锁互斥量,并阻塞到本行,阻塞到其他某个线程调用notify_one()成员函数为止。然后wait函数返回,互斥量上锁
2.带独占锁参数和参数lambda表达式
a.如果第二个参数的lambda表达式返回值是false,那么wait()将解锁互斥量,并阻塞到本行.
阻塞到其他某个线程调用notify_one()成员函数并且lambda表达式为true. wait才能返回
b.如果第二个参数的lambda表达式返回值是true,那么wait()直接返回并继续执行
互斥量被上锁,效果就是调了notify_one效果一样
notify_one
不带参数,wait函数返回,互斥量上锁
带参数,需要等lambda表达式为true,wait函数才返回,互斥量上锁
notify_all
和notify_one一样
wait_until、wait_for
可以指定一个时间段,在当前线程收到通知或者指定的时间 rel_time 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,wait_for 返回。
其返回值有2种状态:timeout和no_timeout 。即std::cv_status::timeout 和std::cv_status::tno_timeout
所以wait_for 函数和windows的等待函数waiforsingleobject函数一样。
- DWORD WINAPI datathread(PVOID p)
- {
- CMainFrame *pframe = (CMainFrame*)p;
- std::unique_lock<std::mutex> lock((pframe->m_mutex));
- while ((pframe->cond.wait_for(lock, std::chrono::seconds(2))) ==
- std::cv_status::timeout)
- {
- int a = 1;
- }
- return 0;
- }
-
- void CMainFrame::OnnewThread()
- {
- std::thread thr(datathread, this);
- thr.detach();
- }
-
- void CMainFrame::Onclosethread()
- {
- std::unique_lock<std::mutex> lock(m_mutex);
- cond.notify_all();
- }
C++11中的std::future是一个模板类。std::future提供了一种用于访问异步操作结果的机制。std::future所引用的共享状态不能与任何其它异步返回的对象共享.
async和packaged_task有一个共同点它们都可以返回一个future对象,用户可以通过这个future的get方法获取最终的结果。
- std::packaged_task<int (int,int)> task(Add);
- future<int> result = task.get_future();
- //启动任务,非异步
- task(4,8);
- cout << "task_thread :" << result.get() << "\n";
. get函数:
(1).当共享状态就绪时,返回存储在共享状态中的值(或抛出异常)。
(2).如果共享状态尚未就绪(即提供者尚未设置其值或异常),则该函数将阻塞调用的线程直到就绪.
wait函数
会让当前线程阻塞,直到子线程返回来。
创建thread线程结合一起使用,使函数对象异步执行。
类似于funciton, packaged_task可以绑定一个可调用对象, 并执行,但是它的返回类型是void,获取它的返回值必须用get_future函数。:
packaged_task类模板也是定义于future头文件中,它包装任何可调用 (Callable) 目标,包括函数、 lambda 表达式、 bind 表达式或其他函数对象,使得能异步调用它,其返回值或所抛异常被存储于能通过 std::future 对象访问的共享状态中。简言之,将一个普通的可调用函数对象转换为异步执行的任务。
常用的函数
1.get_future
得到std::future 的值
2.reset
- //包装可调用目标时lambda
- packaged_task<int(int,int)> task([](int a, int b){ return a + b;});
- //仿函数形式,启动任务,非异步
- task(2, 10);
-
- future<int> result = task.get_future();
- //获取共享状态中的值,直到ready才能返回结果或者异常
- cout << "task_lambda :" << result.get() << "\n";
-
-
- //包装普通函数
- std::packaged_task<int (int,int)> task(Add);
- future<int> result = task.get_future();
-
- //启动任务,非异步
- task(4,8);
- cout << "task_thread :" << result.get() << "\n";
-
-
- //或者通过线程启动任务,异步启动
- thread td(move(task), 2, 10);
- td.join();
- //获取执行结果
- cout << "task_thread :" << result.get() << "\n";
结论:
1.packaged_task你需要自己调用thread,而async创建后,就会默认执行,后面直接调用get方法就好了。
2.想真正实现异步,需要使用线程启动任务。
C++11中的std::async是个模板函数。std::async异步调用函数,在某个时候以Args作为参数(可变长参数)调用Fn,无需等待Fn执行完成就可返回,返回结果是个std::future对象。Fn返回的值可通过std::future对象的get成员函数获取。一旦完成Fn的执行,共享状态将包含Fn返回的值并ready。
std::launch::async 创建新线程执行
- 默认参数为std::launch::async,创建新线程执行
- int cal(int a, int b)
- {
- return a + b;
- }
- std::future<int> result = async(cal,1,2);
- result.wait();
launch::deferred //任务已经创建,线程不创建,不执行函数
std::future
cout< 总结: async和deferred的不同之处是async强制任务创建新线程执行函数,而deferred不是,所以deferred是在调用处中延迟执行任务。与判断联合使用,可以防止线程过多导致系统崩溃。async和packaged_task有一个共同点它们都可以返回一个future对象,用户可以通过这个future的get方法获取最终的结果。延伸:c++11线程池