#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
struct Status
{
int value = 1;
};
void DisplayStatus(const Status&status)
{
cout << "status is " << status.value << endl;
}
void UpdateStatus(Status &status)
{
status.value++;
DisplayStatus(status);
}
class X
{
public:
void do_something(int &n)
{
cout << "do something" << endl;
}
};
int main()
{
Status status;
std::thread t(UpdateStatus, status);//这里的status并没有传入引用,而是传入了一份拷贝
t.join();
DisplayStatus(status);//线程执行完之后,status并没有被改变
std::thread t2(UpdateStatus, ref(status));//这里传递了引用
t2.join();
DisplayStatus(status);//status在线程中被改变
X x;
int n = 5;
//thread t3(&X::do_something, &x, n);这样是错误的
thread t3(&X::do_something, &x,ref(n));//传递一个成员函数的指针作为函数
t3.join();
return 0;
}
std::thread
是可移动的,而非可复制的。所以转移所有权要以移动操作来进行。
explicit
可以防止构造函数隐式自动转换
void some_function()
{
}
void some_other_function()
{
}
int main()
{
std::thread t1(some_function);
std::thread t2 = std::move(t1); //t1的控制权移动到了t2
t1 = std::thread(some_other_function);//从临时对象中进行移动是自动的和隐式的,临时对象的控制权移动到t1
std::thread t3; //没有关联的线程
t3 = std::move(t2); //t2的控制权移动到了t3
t1 = std::move(t3);//会导致程序终止,因为t1已经关联了线程
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
template<typename Iterator, typename T>
struct accumulate_block
{
void operator()(Iterator first, Iterator last, T&result)
{
result = std::accumulate(first, last,result);
}
};
template<typename Iterator,typename T>
T parallel_accumulate(Iterator first, Iterator last, T init)
{
unsigned long const length = std::distance(first, last);//计算两个迭代器之间的距离
if (!length)
{
return init;
}
unsigned long const min_per_thread = 25;
unsigned long const max_threads = (length + min_per_thread - 1) / min_per_thread;
/*
给定程序执行时能够真正并发运行的线程数量的指示,在多核系统上它可能是CPU核心的数量
*/
unsigned long const hardware_threads = std::thread::hardware_concurrency();
unsigned long const num_threads = std::min(hardware_threads != 0 ? hardware_threads : 2, max_threads);
unsigned long const block_size = length / num_threads;
std::vector<T> results(num_threads);
std::vector<std::thread> threads(num_threads - 1);
Iterator block_start = first;
for (unsigned long i = 0; i < (num_threads - 1); ++i)
{
Iterator block_end = block_start;
std::advance(block_end, block_size); //将迭代器移动一定的距离
threads[i] = std::thread(accumulate_block<Iterator,T>(),block_start, block_end, ref(results[i]));
block_start = block_end;
}
accumulate_block<Iterator,T>()(block_start, last, results[num_threads - 1]);
std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
return std::accumulate(results.begin(), results.end(), init);
}
int main()
{
std::vector<int> v;
for (int i = 1; i <= 99; i++)
{
v.push_back(i);
}
int result = parallel_accumulate<decltype(v.begin()),int>(v.begin(), v.end(),0);
cout << result << endl;
return 0;
}
其他知识点:关于std::mem_fn
class ClassA
{
public:
void do_something(int n)
{
}
};
int main()
{
ClassA a;
auto memeber_fun = std::mem_fn(&ClassA::do_something);
memeber_fun(a,1);
return 0;
}
竞争条件一般是时间敏感的,他们常常在应用程序运行于调试工具下时完全消失,因为调试工具会影响程序的时间,即使是轻微的。
死锁的四个条件
解决死锁的方案
使用std::lock
同时锁定多个锁
std::adopt_lock
告知std::lock_guard
对象该互斥元已被锁定,并且他们只应沿用互斥元上已有锁的所有权,而不是试图在构造函数中锁定互斥元
class some_big_object {};
void swap(some_big_object &lhs, some_big_object &rhs);
class X
{
private:
some_big_object some_detail;
std::mutex m;
public:
X(const some_big_object&sd ):some_detail(sd){}
friend void swap(X &lhs, X&rhs)
{
if (&lhs == &rhs)
{
return;
}
std::lock(lhs.m, rhs.m); //同时锁定多个锁
std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
//std::adopt_lock告知std::lock_guard对象该互斥元已被锁定,并且他们只应沿用互斥元上已有锁的所有权,而不是试图在构造函数中锁定互斥元
swap(lhs.some_detail, rhs.some_detail);
}
};
std::unique_lock
灵活锁定std::unique_lock
占用更多的空间并且使用起来比std::lock_guard
略慢,但是更加灵活std::unique_lock
提供了lock()
、try_lock()
和unlock()
三个成员函数。所以可以随时解锁,而不必非要等到析构。class some_big_object {};
void swap(some_big_object &lhs, some_big_object &rhs);
class X
{
private:
some_big_object some_detail;
std::mutex m;
public:
X(const some_big_object&sd ):some_detail(sd){}
friend void swap(X &lhs, X&rhs)
{
if (&lhs == &rhs)
{
return;
}
std::unique_lock<std::mutex> lock_a(lhs.m, std::defer_lock);
std::unique_lock<std::mutex> lock_b(rhs.m, std::defer_lock);
std::lock(lock_a, lock_b);
swap(lhs.some_detail, rhs.some_detail);
}
};
std::unique_lock
主要用于以下三种场景
class some_resource
{
public:
void do_something(){}
};
std::shared_ptr<some_resource> resource_ptr;
std::mutex resource_mutex;
//安全,但是序列化问题足够大
void foo()
{
std::unique_lock<std::mutex> lock(resource_mutex);
if (!resource_ptr)
{
resource_ptr.reset(new some_resource);
}
lock.unlock();
resource_ptr->do_something();
}
//臭名昭著的二次检查锁定,会有问题
//就算一个线程看到另一个线程写入指针,它也可能无法看到新创建的some_resource实例,即虽然指针不为空,但是还没有被初始化
void undefined_behaviour_with_double_checked_locking()
{
if (!resource_ptr)
{
std::lock_guard<std::mutex> lock(resource_mutex);
if (!resource_ptr)
{
resource_ptr.reset(new some_resource);
}
}
resource_ptr->do_something();
}
使用std::call_once
可以完美解决上述问题
class some_resource
{
public:
void do_something(){}
};
std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag;
void init_resource()
{
resource_ptr.reset(new some_resource);
}
void foo()
{
std::call_once(resource_flag, init_resource);
resource_ptr->do_something();
}
比如有些数据很少更新,但是经常会读。这种情况下使用std::mutex
就太悲观了,意味着不能同时读了。此时可以使用shared_mutex
来实现解决读写问题,类似pthread的读写锁。std::shared_mutex
在C++17中才实现,考虑到现在大部分编译器都还不支持,所以实际上都是使用boost::shared_mutex
。
shared_mutex
来同步,而不是mutex
std::unique_lock
或std::lock_guard
来独占锁,即写锁boost::shared_lock
来共享锁,即读锁。class dns_entry{};
class dns_cache
{
std::map<std::string, dns_entry> entries;
mutable std::shared_mutex entry_mutex; //mutable突破const的限制,即使再const函数中也可被改变
public:
dns_entry find_entry(std::string const &domain) const
{
std::shared_lock<std::shared_mutex> lock(entry_mutex);
const auto it = entries.find(domain);
return(it == entries.end()) ? dns_entry() : it->second;
}
void update_or_add_entry(const std::string&domain, const dns_entry&entry)
{
std::lock_guard<std::shared_mutex>lock(entry_mutex);
entries[domain] = entry;
}
};
在使用std::mutex
的情况下,一个线程试图锁定其已经拥有的互斥元是错误的,并且试图这么做导致未定义行为。
C++提供了std::recursive_mutex
,可以在同一个线程中的单个实例上获取多个锁。在互斥元能够被另一个线程锁定前,必须释放所有的锁。
大多数时间,如果你觉得需要一个递归互斥元,你可能反而需要改变你的设计。
struct data_chunk{};
bool more_data_to_prepare() { return true; }
bool is_last_chunk(data_chunk) { return false; }
data_chunk prepare_data() { return data_chunk(); };
void process(data_chunk) {};
std::mutex mut;
std::queue<data_chunk>data_queue;
std::condition_variable data_cond;
void data_preparation_thread()
{
while (more_data_to_prepare())
{
const data_chunk data= prepare_data();
std::lock_guard<std::mutex> lock(mut);
data_queue.push(data);
data_cond.notify_one(); //通知等待中的线程
}
}
void data_processing_thread()
{
while (true)
{
std::unique_lock<std::mutex> lk(mut);
data_cond.wait(lk, [] {return !data_queue.empty(); });//获取锁,查条件,条件不满足则释放锁继续等
data_chunk data = data_queue.front();
data_queue.pop();
lk.unlock();
process(data);
if(is_last_chunk(data))
break;
}
}
future
等待一次性事件std::async
可以传入参数来决定是否启动一个新线程
std::launch::async
:在新线程中运行std::launch::deferred
:在wait()
或get()
中运行std::launch::async|std::launch::deferred
(默认):由具体实现来选择struct X
{
void foo(int, std::string const &) {};
std::string bar(const std::string &) { return std::string(); };
};
X x;
auto f1 = std::async(&X::foo, &x, 42, "hello");//调用p->foo(42,"hello"),其中p是&x
auto f2 = std::async(&X::bar, x, "goodbye"); //调用tempx.bar("goodbye"),其中tempx是x的副本,所以不会对x有影响
struct Y
{
double operator()(double) { return 0; };
};
Y y;
auto f3 = std::async(Y(), 3.141);//调用tempy(3.141),其中tempy是从Y()移动构造的
auto f4 = std::async(std::ref(y), 2.718);//调用y(2.718)
X baz(X&) {};
auto f5 = std::async(baz, std::ref(x));//调用baz(x)
future
相关联std::mutex m;
std::deque<std::packaged_task<void()>> tasks;
std::condition_variable task_cond;
bool gui_shutdown_message_recived();
void get_and_process_gui_message();
void gui_thread()
{
while (!gui_shutdown_message_recived())
{
get_and_process_gui_message();
std::unique_lock<std::mutex> lk(m);
task_cond.wait(lk, [] {return !tasks.empty(); });
std::packaged_task<void()> task = std::move(tasks.front());
tasks.pop_front();
lk.unlock();
task();
}
}
std::thread gui_bg_thread(gui_thread);
template<typename Func>
std::function<void> post_task_for_gui_thread(Func f)
{
std::packaged_task<void()> task(f);
std::future<void> res = task.get_future();
std::lock_guard<std::mutex>lk(m);
tasks.push_back(std::move(task));
task_cond.notify_one();
return res;
}
std::promise
参考:C++11多线程-异步运行(1)之std::promise
void read(std::future<std::string> *future) {
// future会一直阻塞,直到有值到来
std::cout << future->get() << std::endl;
}
int main() {
// promise 相当于生产者
std::promise<std::string> promise;
// future 相当于消费者, 右值构造
std::future<std::string> future = promise.get_future();
// 另一线程中通过future来读取promise的值
std::thread thread(read, &future);
// 让read等一会儿:)
std::this_thread::sleep_for(seconds(1));
//
promise.set_value("hello future");
// 等待线程执行完成
thread.join();
return 0;
}
future
保存异常可以通过some_promise.set_exception()
来让future抛出异常
std::future
而言,只有一个线程可以获取值,因为在首次调用get()
后,就没有任何可获取的值留下了。std::shared_future
对象来访问该状态,那么就是安全的。std::chrono::system_clock
是系统时钟,是不匀速(steady)时钟,因为系统时钟可以调整。std::chrono::system_clock::now()
返回的是当前时间。std::chrono::steady_clock
是匀速时钟std::chrono::high_resolution_clock
提供的是所有类库时钟中最小可能的节拍周期(和可能的最高精度),它实际上可能是其他时钟之一的typedef
,实际上,在vs2015中,该时钟就是steadt_clock
的typedef
时间段由std::chrono::duration<>
来表示,实际上我们常用的milliseconds
等都是duration
的typedef
。在VS2015中有以下定义:
typedef ratio<1, 1000000000000000000LL> atto;
typedef ratio<1, 1000000000000000LL> femto;
typedef ratio<1, 1000000000000LL> pico;
typedef ratio<1, 1000000000> nano;
typedef ratio<1, 1000000> micro;
typedef ratio<1, 1000> milli;
typedef ratio<1, 100> centi;
typedef ratio<1, 10> deci;
typedef ratio<10, 1> deca;
typedef ratio<100, 1> hecto;
typedef ratio<1000, 1> kilo;
typedef ratio<1000000, 1> mega;
typedef ratio<1000000000, 1> giga;
typedef ratio<1000000000000LL, 1> tera;
typedef ratio<1000000000000000LL, 1> peta;
typedef ratio<1000000000000000000LL, 1> exa;
// duration TYPEDEFS
typedef duration<long long, nano> nanoseconds;
typedef duration<long long, micro> microseconds;
typedef duration<long long, milli> milliseconds;
typedef duration<long long> seconds;
typedef duration<int, ratio<60> > minutes;
typedef duration<int, ratio<3600> > hours;
基于时间段的等待使用类库内部的匀速时钟来衡量时间,因此35毫秒意味着35毫秒的逝去时间,即便系统时钟在等待期间进行调整。
std::chrono::time_point<>
类模板的实例来表示的,第一个模板参数指定其参考的时钟,第二个模板参数指定计量单位。计算两个时间点之间长度的时间段
auto start = std::chrono::high_resolution_clock::now();
do_sometiong();
auto stop = std::chrono::high_resolution_clock::now();
std::cout << "do_something() took " << std::chrono::duration<double, std::chrono::seconds>(stop - start).count() << " seconds" << std::endl;
等待一个具有超时的条件变量
bool wait_loop()
{
auto const timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(500);//匀速时钟,不可变
std::unique_lock<std::mutex> lk(m);
while (!done)
{
if(cv.wait_until(lk,timeout)==std::cv_status::timeout)
break;
}
return true;
}
bool wait_loop2()
{
auto timeout = std::chrono::duration<long long, std::ratio<1,1000>>(20);
std::unique_lock<std::mutex> lk(m);
while (!done)
{
if(cv.wait_for(lk,timeout)==std::cv_status::timeout)
break;
//等价于:
if (cv.wait_for(lk, std::chrono::microseconds(20))==std::cv_status::timeout)
break;
}
return true;
}
类/命名空间 | 函数 | 返回值 |
---|---|---|
std::this_thread 命名空间 |
sleep_for(duration) sleep_until(time_point) |
不可用 |
std::condition_variable 或std::condition_variable_any |
wait_for(lock,duration) wait_until(lock,time_point) |
std::cv_status::timeout 或std::cv_status::no_timeout |
wait_for(lock,duration,predicate) wait_until(lock,time_point,predicate) |
bool ——当唤醒时predicate的返回值 |
|
std::timed_mutex 或std::recursive_timed_mutex |
try_lock_for(duration) try_lock_until(time_point) |
bool—true如果获得了锁,否则false |
std::unique_lock |
||