原子类型std::atomic<T>可以使用类型做为模板,为了方便大家的使用C++11中内置了整型的原子变量。使用原子变量就不需要和互斥量配合使用,使用后的代码将更加简洁。下面的代码使用原子变量实现整型数值的自增操作。
const int MAX_COUNT=3;
atomic<int> n(0);
void increase_n()
{
for(int i=0;i<MAX_COUNT;i++)
{
++n;
}
}
int main()
{
thread t1(increase_n);
thread t2(increase_n);
t1.join();
t2.join();
cout<<n<<endl;
return 0;
}
如上代码所示,最后程序输出结果为6;
原子变量和互斥量性能对比
下面,将对上面的代码进行改造,比较下分别使用原子变量和互斥变量,比较下他们的性能,代码一是对上面的代码简单改造,代码和运行结果如下:
const int MAX_COUNT = 1e6;
atomic<int> n(0);
void increase()
{
for (int i = 0; i < MAX_COUNT; i++)
{
++n;
}
}
int main(void)
{
ULONGLONG ullBegin = GetTickCount64();
cout << "开始时CPU时钟" << ullBegin << endl;
thread t1(increase);
thread t2(increase);
t1.join();
t2.join();
cout << n << endl;
ULONGLONG ullDelay = GetTickCount64()-ullBegin;
cout << "实行时间CPU时钟" << ullDelay << endl;
return 0;
}
运行结果如下:
改成互斥量的代码为:
utex g_mutex;
int sum(0);
void increase_mutex()
{
for (int i = 0; i < MAX_COUNT; i++)
{
g_mutex.lock();
++sum;
g_mutex.unlock();
}
}
int main(void)
{
ULONGLONG ullBegin = GetTickCount64();
cout << "开始时CPU时钟" << ullBegin << endl;
thread t1(increase_mutex);
thread t2(increase_mutex);
t1.join();
t2.join();
cout << n << endl;
ULONGLONG ullDelay = GetTickCount64()-ullBegin;
cout << "实行时间CPU时钟" << ullDelay << endl;
return 0;
}
代码运行结果为:
综上:由原子和互斥变量运行结果可知,使用原子变量比使用互斥量性能要提升3.8倍。
call_once/once_flag的使用
在实际编程时,如果有变量或者函数需要被初始化或者执行一次,那么就可以使用call_once来保障了,在C++11中std::call_once用来保证在多线程运行环境下函数或者变量只被执行或者初始化一次,在使用call_once时,需要配合once_flag进行使用,使用方法也相对简单,代码示例如下:
int winner;
void set_winner (int x) { winner = x; std::cout << "winner thread: " << winner << '\n';}
std::once_flag winner_flag;
void wait_1000ms (int id) {
// count to 1000, waiting 1ms between increments:
for (int i=0; i<1000; ++i)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
// claim to be the winner (only the first such call is executed):
std::call_once (winner_flag,set_winner,id);
}
int main ()
{
std::thread threads[10];
// spawn 10 threads:
for (int i=0; i<10; ++i)
threads[i] = std::thread(wait_1000ms,i+1);
std::cout << "waiting for the first among 10 threads to count 1000 ms...\n";
for (auto& th : threads) th.join();
return 0;
}
在上面的代码中set_winner 只被运行了一次,id为获取到这个函数执行权限的线程序号。运行结果为:
waiting for the first among 10 threads to count 1000 ms...
winner thread: 6
需要说明的是每次执行代码时获取到函数执行权限的线程序号会存在不同。所以每次运行的结果也可能不同。