C++11的线程并发要点
1.线程构建
a.通过 std::thread(funPoint,param1,param2) 来创建.当构造函数的参数为空时.默认thread是joinable = false的.
2.线程的行为
a.join
b.attach
3.线程参数的注意事项
==a==.注意如下语句:
void oops(int some_param)
{
char buffer[1024]; // 1
sprintf(buffer, "%i",some_param);
std::thread t(f,3,buffer); // 2
t.detach();
}
void not_oops(int some_param)
{
char buffer[1024];
sprintf(buffer,"%i",some_param);
std::thread t(f,3,std::string(buffer)); // 使用std::string,避免悬垂指针
t.detach();
}
原因: buffer②是一个指针变量,指向本地变量,然后本地变量通过buffer传递到新线
程中②。并且,函数有很大的可能,会在字面值转化成 std::string 对象之前崩溃(oops),从
而导致线程的一些未定义行为。解决方案就是在传递到 std::thread 构造函数之前就将字面值
转化为 std::string 对象。
==b==. std::unique_ptr 和std::move
void process_big_object(std::unique_ptr<big_object>);
std::unique_ptr<big_object> p(new big_object);
p->prepare_data(42);
std::thread t(process_big_object,std::move(p));
"移动"是指:原始对象中的数据转移给另一对象,而转移的这些数据就不再在原始对象中保存了. std::unique_ptr 就是这样一种类型(译者:C++11中的智能指针),这种类型
为动态分配的对象提供内存自动管理机制(译者:类似垃圾回收)。同一时间内,只允许一个 std::unique_ptr 实现指向一个给定对象,并且当这个实现销毁时,指向的对象也将被删除。
==c==. std::thread::id 或者 std::this_thread::get_id()可以用来区分是否主线程.
4.线程锁
==a==.我们要引入互斥量mutex和为了方便使用互斥量的std::lock_guard.当我们要保护一个变量免予同时修改的时候.我们使用:std::lock_gard<std::mutex> guard(some_mutex) 来进行.
==b==.层次锁:先获取高级别的锁.再获取低级别的锁---符合要求.
==c==.std::unique_lock 和std:unique_guard一样.区别在于更灵活.使用都是:std::unique_lock<std::mutxt> m_lok(m_mutex); 灵活性体现在.比如可以转移所有权.如例子:
std::unique_lock<std::mutex> get_lock()
{
extern std::mutex some_mutex;
std::unique_lock<std::mutex> lk(some_mutex);
prepare_data();
return lk; // 1
}
void process_data()
{
std::unique_lock<std::mutex> lk(get_lock()); // 2 这里就是用1的unique_lock
do_something();
}
==d==.双重锁.不建议使用.所以C11增加了一个.std::once_flag 和std::call_once
5.线程同步
==a==. std:condition_variable 这个可以用来阻塞线程.采用wait表示阻塞。采用notify_all表示唤醒。如下代码
std::condition_variable cv; // 全局条件变量.
bool ready = false; // 全局标志位.
void do_print_id(int id)
{
std::unique_lock <std::mutex> lck(mtx);
while (!ready) // 如果标志位不为 true, 则等待...
cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后,
// 线程被唤醒, 继续往下执行打印线程编号id.
std::cout << "thread " << id << '\n';
}
void go()
{
std::unique_lock <std::mutex> lck(mtx);
ready = true; // 设置全局标志位为 true.
cv.notify_all(); // 唤醒所有线程.
}
==b==.std::future 从异步对象中返回值。一个简单的例子
std::future<bool> fut = std::async(is_prime, 700020007); //future一般是一个异步对象
std::cout << "please wait";
std::chrono::milliseconds span(100); //设置一个超时时间
while (fut.wait_for(span) != std::future_status::ready) //等待超时时间.超时时间内如果有给结果.那么就有一个std::future_status::read
std::cout << ".";
std::cout << std::endl;
bool ret = fut.get(); //获取超时对象给予的值
==c==:std::packaged_task 异步对象以及处理。因线程本身不返回值。我们可以把函数打包到一个线程中。再用std::future来获取得到。
include <iostream>
#include <future>
#include <chrono>
#include <functional>
int Test_Fun(int a, int b, int &c)
{
//a=1,b=2,c=0
//突出效果,休眠5s
std::this_thread::sleep_for(std::chrono::seconds(5));
//c=233
c = a + b + 230;
return c;
}
int main()
{
//声明一个std::packaged_task对象pt1,包装函数Test_Fun
std::packaged_task<int(int, int, int&)> pt1(Test_Fun);
//声明一个std::future对象fu1,包装Test_Fun的返回结果类型,并与pt1关联
std::future<int> fu1 = pt1.get_future();
//声明一个变量
int c = 0;
//创建一个线程t1,将pt1及对应的参数放到线程里面执行
std::thread t1(std::move(pt1), 1, 2, std::ref(c));
//阻塞至线程t1结束(函数Test_Fun返回结果)
int iResult = fu1.get();
std::cout << "执行结果:" << iResult << std::endl; //执行结果:233
std::cout << "c:" << c << std::endl; //c:233
return 1;
}
==d==: std::promise 提供了一种方式。用于不同的线程间可以共享数据。这个数据可以进行计算。入门例子如下
#include <iostream>
#include <future>
#include <chrono>
void Thread_Fun1(std::promise<int> &p)
{
//为了突出效果,可以使线程休眠5s
std::this_thread::sleep_for(std::chrono::seconds(5));
int iVal = 233;
std::cout << "传入数据(int):" << iVal << std::endl;
//传入数据iVal
p.set_value(iVal);
}
void Thread_Fun2(std::future<int> &f)
{
//阻塞函数,直到收到相关联的std::promise对象传入的数据
auto iVal = f.get(); //iVal = 233
std::cout << "收到数据(int):" << iVal << std::endl;
}
int main()
{
//声明一个std::promise对象pr1,其保存的值类型为int
std::promise<int> pr1;
//声明一个std::future对象fu1,并通过std::promise的get_future()函数与pr1绑定
std::future<int> fu1 = pr1.get_future();
//创建一个线程t1,将函数Thread_Fun1及对象pr1放在线程里面执行
std::thread t1(Thread_Fun1, std::ref(pr1));
//创建一个线程t2,将函数Thread_Fun2及对象fu1放在线程里面执行
std::thread t2(Thread_Fun2, std::ref(fu1));
//阻塞至线程结束
t1.join();
t2.join();
return 1;
因为future后的get函数.获取后其他线程就获取不到了.没办法让多个线程等待同一个事件.这时候就需要std::shared_future了.简单的例子如下:
#include <iostream> // std::cout
#include <future> // std::async, std::future, std::shared_future
int do_get_value() { return 10; }
int main ()
{
std::future<int> fut = std::async(do_get_value);
std::shared_future<int> shared_fut = fut.share();
// 共享的 future 对象可以被多次访问.
std::cout << "value: " << shared_fut.get() << '\n';
std::cout << "its double: " << shared_fut.get()*2 << '\n';
return 0;
}
时间周期
==a==.
std::chrono::system_clock::now() 是将返回系统时钟的当前时间.chrono::duration_cast则可以进行时间周期的转换。如下例子:
std::chrono::minutes t1( 10 );
std::chrono::seconds t2( 60 );
std::chrono::seconds t3 = t1 - t2;
std::cout << t3.count() << " second" << std::endl;
//接下来把秒转化为分
out << chrono::duration_cast<chrono::minutes>( t3 ).count() <<” minutes”<< endl;
//将会输出: 9 minutes
std::chrono::time_point 用于解决时间戳的问题,比如我们计算耗时:
#include <iostream>
#include <iomanip>
#include <ctime>
#include <chrono>
int main()
{
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t(now - std::chrono::hours(24));
std::cout << "24 hours ago, the time was "
<< std::put_time(std::localtime(&now_c), "%F %T") << '\n';
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
std::cout << "Hello World\n";
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::cout << "Printing took "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
<< "us.\n";
==c==.
RxCpp 响应式编程.
==d==. 原子操作
std::atomic_flag 原子对象没有拷贝和赋值构造函数.
网友评论