C++0x概览:多线程(3)
副标题#e#
在初始化时掩护数据
假如你的数据需要在初始化时被掩护,就不能再利用mutex了。因为在初始化竣事后,这会引起不须要的同步。C++0x提供了许多要领来在初始化时掩护数据。
1)假定你的结构函数是用constexpr要害字声明而且满意常量初始化的条件。在这种环境下,一个静态存储区的工具在静态初始阶段会确保在其他代码运行之前被初始化。对付std::mutex来说,这是最佳选择,因为它消除了全局mutex初始化时发生紊乱的大概性。
class my_class
{
int i;
public:
constexpr my_class():i(0){}
my_class(int i_):i(i_){}
void do_stuff();
};
my_class x; // static initialization with constexpr constructor
int foo();
my_class y(42+foo()); // dynamic initialization
void f()
{
y.do_stuff(); // is y initialized?
}
2)在一个块浸染域(block scope)中利用静态变量。在C++0x中,块浸染域的静态变量在函数第一次被挪用时初始化。假如另一个线程在初始化完成之前试图挪用该函数,它必需期待。
void bar()
{
static my_class z(42+foo()); // initialization is thread-safe
z.do_stuff();
}
3)假如以上环境都不合用(工具大概是动态建设),那么最好利用std::call_once和std::once_flag。从名字就可以看出,std::call_once用于与一个std::once_flag实例协作,指定的函数将只会执行一次。
my_class* p=0;
std::once_flag p_flag;
void create_instance()
{
p=new my_class(42+foo());
}
void baz()
{
std::call_once(p_flag,create_instance);
p->do_stuff();
}
同std::thread结构函数一样,std::call_once也可以接管函数工具作为参数,而且接管多个参数。再次强调,默认是传拷贝。假如要传引用,请利用std::ref.
#p#副标题#e#
期待事件
假如想在线程间共享数据,凡是需要一个线程期待另一个线程执行某些操纵。我们但愿这不要耗费CPU时间。假如线程只是期待会见共享数据,那mutex锁就足够了。不外,这样做有时并达不到想要的功效。
最简朴的要领是让线程Sleep一段时间,然后去查抄是否可以举办想要的操纵。必然要确保你用来掩护指示事件已经产生的数据的mutex在线程休眠的时候已经unlock(这话是不是听着很别扭?呵呵,我也不知道怎么翻译会容易让人领略一些。不外看了下面这段代码,相信你会大白的):
std::mutex m;
bool data_ready;
void process_data();
void foo()
{
std::unique_lock<std::mutex> lk(m);
while(!data_ready)
{
lk.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
lk.lock();
}
process_data();
}
这是最简朴的要领,可是并欠好。有两个原因。第一,在数据筹备好之后,线程在被叫醒后去查抄数据之前平均需要期待5毫秒。这有时会造成明明的滞后。尽量可以通过淘汰期待时间来办理,可是这会带来第二个问题:每隔10毫秒,线程必需醒来,得到mutex,查抄flag,纵然什么也没有产生。这将淹灭CPU时间和增加对mutex的抢占。因此,这将潜在地减慢了正在期待的线程去执行任务。
假如你发明你的代码是那么写的,那么请用条件变量来取代。不要让线程期待一段确定的时间,你可以让线程休眠直到收到另一个线程通知。这可以有效地让期待线程的CPU利用率为0。我们可以用条件变量来重写foo函数:
std::mutex m;
std::condition_variable cond;
bool data_ready;
void process_data();
void foo()
{
std::unique_lock<std::mutex> lk(m);
while(!data_ready)
{
cond.wait(lk);
}
process_data();
}
留意上面的代码把 lock工具lk作为参数传给了wait()函数。条件变量在wait()函数的unlock这个mutex,在退出函数的时候再将其lock。这样可以确保线程在休眠时被掩护的数据可以被其他线程修改。配置data_ready符号的代码可以这样写:
void set_data_ready()
{
std::lock_guard<std::mutex> lk(m);
data_ready=true;
cond.notify_one();
}
你仍然需要查抄数据是否已经筹备好,因为条件变量有大概被恶意叫醒,此时wait()函数将返回尽量它没有被另一个线程通知。假如你担忧这种环境,你可以让尺度库来帮你搞定。你只需要指明你在期待什么即可。
void foo()
{
std::unique_lock<std::mutex> lk(m);
cond.wait(lk,[]{return data_ready;});
process_data();
}