C++11之 Thread线程库

C++‘s Most Vexing Parse
凡是长的像函数声明的,都是函数声明。

常常出现在类的构造中使用匿名对象的时候。

string mystr();
vector<int> my_ivec();
int b(int()); 
// 这些都是函数声明

 

解决办法:
将匿名对象使用括号扩起来,作为一个整体
使用C++11的初始化语法 {}


启动线程

#include <iostream>
#include <thread>

using std::cout;
using std::thread;

class background_task
{
public:
void operator()() {
do_something();
do_something_else();
}

private:
void do_something();
void do_something_else();
};

void background_task::do_something() {
cout<<__FUNCTION__<<": exec...\n";
}

void background_task::do_something_else() {
cout<<__FUNCTION__<<": exec...\n";
}


int main()
{

//    background_task f;
//    thread my_thread(f);
//
//    my_thread.join();


// error example
// my_thread has no member function of join
// C++'s most vexing parse
// it is translated as
// thread my_thread( background_task (*pn)() );
// it's a function declaration.

//thread my_thread(background_task());
//my_thread.join();

//    thread my_thread((background_task()));
//    my_thread.join();

thread my_thread{background_task()};
my_thread.join();

return 0;
}

 

也可以传递lambda表达式来启动线程

#include <iostream>
#include <thread>

using std::cout;
using std::thread;

void do_something()
{
cout<<__FUNCTION__<<": exec ...\n";
}

void do_something_else()
{
cout<<__FUNCTION__<<": exec ...\n";
}


int main()
{
thread my_thread([] {
do_something();
do_something_else();
});

my_thread.join();
return 0;
}

 

 

启动线程后,需要明确是要等待线程结束(加入式),还是让其自主运行(分离式)。需要在thread对象销毁前决定。

#include <iostream>
#include <thread>
#include <string.h>

using std::cout;
using std::thread;

void do_something(int& pi) {
cout<<pi<<"\t";
}

struct func {
int& pi;
public:
func(int& _pi): pi(_pi) {}
~func() {

}
void operator() () {
int j = 0;
for(;j<100000;j++) {
do_something(pi);
}
}

};

void oops(){
int local_variable = 0;

thread my_thread{func(local_variable)};

//my_thread.detach();
my_thread.join();
}

int main()
{
oops();


for(int i = 0; i < 1000; i++) {
int* p = new int[100]();
memset(p,50,100* sizeof(int));
delete[] p;
}
return 0;

}

 

这段代码中,线程函数对象中的引用(或指针)指向的是oops()函数中的局部变量,当使用detach时,oops()函数结束时,线程函数还在运行中,导致在对象中使用了非法的引用,导致数据上的错误。

如果一个可调用对象作为线程函数,对于对象中包含指针或引用时,需要特别的谨慎。


线程的异常安全问题

在有异常的情况下,正确的等待线程函数的退出。 在函数f()中,因为调用的div_s会抛出异常,如果f()中进行捕获过滤,则可以通过最后的join()退出线程函数,如果f()中不进行处理,而是像上一级抛出异常,则需要先退出线程函数,即在throw 之前调用join()函数。

#include <iostream>
#include <thread>
#include <string.h>
#include <unistd.h>

using std::thread;
using std::cout;


int div_s(int denum, int num){

cout<<denum<<" / "<<num<<"\n";
if(num) {
return denum / num;
}

throw num;
}

void do_something(int& i)
{    
for(int j = 0; j < 100000; j++)
{
if(i) {
cout<<"err happend.\n";
}
}

cout<<"bye bye.\n";
}

struct func{
int& i;
func(int& i_):i(i_) {}

void operator() () {
do_something(i);
}

};


void f()
{
int some_local_state = 0;
thread my_thread{func(some_local_state)};

try
{
div_s(10,2);
div_s(8,4);
div_s(5,0);
div_s(3,1);
}
catch(...)
{
cout<<"catch exception.\n";
//如果这里需要往上抛出异常,则需要先调用join,等待线程函数退出,然后再抛出异常
my_thread.join();
throw;
}
cout<<"here!\n";
my_thread.join();
}


int main()
{
try{
f();
}
catch(...) {
cout<<"catch exception in main\n";
}

for(int i = 0; i < 1000; i++)
{
int* p = new int[100];
memset(p,0,sizeof(p));
}

return 0;
}

 

另一种思路是使用RAII(Resource Acquisition is Initialization 资源获取即初始化方式)
注意将封装类的赋值构造函数和赋值运算符重载给删除。

#include <iostream>
#include <thread>
#include <string.h>
#include <unistd.h>

using std::thread;
using std::cout;


int div_s(int denum, int num){

cout<<denum<<" / "<<num<<"\n";
if(num) {
return denum / num;
}

throw num;
}

void do_something(int& i)
{    
for(int j = 0; j < 100000; j++)
{
if(i) {
cout<<"err happend.\n";
}
}

cout<<"bye bye.\n";
}

struct func{
int& i;
func(int& i_):i(i_) {}

void operator() () {
do_something(i);
}

};


class thread_guard
{
public:
explicit thread_guard(thread& t_):t(t_) {}
~thread_guard() {

if(t.joinable()) {
t.join();
}
}

thread_guard(const thread_guard& ) = delete;
thread_guard& operator= (const thread_guard& )= delete;

private:
thread& t;
};


void do_something_in_current_thread() {
div_s(10,2);
div_s(8,4);
div_s(5,0);
div_s(3,1);
}

void f()
{
int some_local_state = 0;
thread my_thread{func(some_local_state)};

thread_guard g(my_thread);
do_something_in_current_thread();
}


int main()
{
try{
f();
}
catch(...) {
cout<<"catch exception in main\n";
}

for(int i = 0; i < 1000; i++)
{
int* p = new int[100];
memset(p,0,sizeof(p));
}

return 0;
}

 

通常称分离线程为守护线程(daemon threads), 没有任何显示的用户接口,并在后台运行的线程。长时间运行,比如在后台监视文件系统,对缓存进行清理等工作。发后即忘(fire and forget).
检查是否可以调用detach 是调用joinable()函数,返回true就可以调用detach.

向线程函数传递参数时,默认参数要拷贝到线程独立内存中,即使参数是引用的形式。
线程函数参数是引用时,必须加const修饰符修饰,要想使参数是引用,并达到修改的效果,传递给线程函数参数时,需要使用std::ref()进行传递参数。

#include <iostream>
#include <thread>
#include <string>

using namespace std;

void f(int i, string const& s)
{
cout<<i<<"\t"<<s<<"\n";
}


void oops(int some_param)
{
char buffer[1024];
sprintf(buffer,"%i",some_param);

thread t(f,3,string(buffer));
//thread t(f,3,buffer); // 有问题,因为buffer是局部变量,detach后oops可能在线程函数执行完成前结束,可以使用string提前转化成string对象
t.detach();
}

struct Test_data {

int x;
int y;
int z;
};


void update_data(struct Test_data& data)
{
data.x += 5;
data.y += 5;
data.z += 5;
}

void oops_again()
{
struct Test_data data;
data.x = 0; data.y = 0; data.z = 0;

thread t(update_data,std::ref(data));
t.join();

cout<<"after update data = \n"<<data.x<<"\t"
<<data.y<<"\t"<<data.z<<"\n";

}


int main()
{
oops(5);

thread t(f,3,"hello");
t.join();

oops_again();

return 0;
}

 


指针可以传递,没有问题,也能达到修改的目的。


线程作为函数的返回值

#include <iostream>
#include <thread>

using std::thread;
using std::cout;


void f1()
{
cout<<__FUNCTION__<<" called.\n";
}

void f2(int num)
{
cout<<__FUNCTION__<<" called, num = "<<num<<"\n";
}

thread f()
{
return thread(f1);
}

thread g()
{
return thread (f2,45);
}

int main()
{

thread t = f();
t.join();

thread t1 = g();
t1.join();

return 0;
}

 

线程作为函数参数

#include <iostream>
#include <thread>
#include <unistd.h>

using std::thread;
using std::cout;

void work()
{
cout<<__FUNCTION__<<" called\n";
int cnt = 0;
while(cnt ++ < 5) {
sleep(1);
printf("cnt = %d\n",cnt);
}
}

void f(thread t)
{
cout<<__FUNCTION__<<" called\n";
if(t.joinable()) t.join();
}

void g()
{
f(thread(work));
thread t (work);

f(std::move(t));
}

int main()
{

g();
return 0;
}

 


多线程并行计算模型

#include <iostream>
#include <algorithm>
#include <thread>
#include <future>
#include <vector>

using std::cout;
using std::thread;
using std::vector;
using std::promise;
using std::future;

int nums[100];

void do_work(unsigned int id, std::promise<int>& proObj)
{
int sum = 0;

for(int i = id * 10 ; i <= id * 10 + 9; i++) {
sum += nums[i];
}
proObj.set_value(sum);
}

void f()
{
vector<thread> threads;
vector<promise<int> > proObjs(10);
vector<future<int> > futureObjs(10);

for(unsigned i = 0; i < 10; i ++) {
futureObjs[i] = proObjs[i].get_future();
threads.push_back(thread(do_work,i,std::ref(proObjs[i])));
}

int sum = 0;

for(int i = 0; i < 10; i ++) {
sum += futureObjs[i].get();
}

for_each(threads.begin(),threads.end(),
std::mem_fn(&std::thread::join));

printf("sum = %d\n",sum);
}

int main()
{
for(int i = 0; i < 100; i ++) {
nums[i] = i + 1; 
}

f();
return 0;
}

 

多核系统中CPU核芯数量

#include <iostream>
#include <thread>

using namespace std;

int main()
{
int cnt = std::thread::hardware_concurrency();
cout<<cnt<<endl;

return 0;
}

# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 
# 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数

# 查看物理CPU个数
cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l

# 查看每个物理CPU中core的个数(即核数)
cat /proc/cpuinfo| grep "cpu cores"| uniq

# 查看逻辑CPU的个数
cat /proc/cpuinfo| grep "processor"| wc -l

查看CPU信息(型号)
cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c

 

获取线程id std::thread::id类型

thread对象的get_id()函数
std::this_thread::get_id()函数

运行时决定线程数目

#include <iostream>
#include <thread>
#include <numeric>
#include <algorithm>
#include <vector>

using namespace std;

template <typename Iterator, typename T>
struct accumulate_block
{
void operator() (Iterator first, Iterator last, T& result)
{
result = accumulate(first,last,result);
}
};

template <typename Iterator, typename T>
T parallel_accumulate(Iterator first, Iterator last, T init)
{
const unsigned long length = std::distance(first,last);

if(!length) return init;

const unsigned long min_per_thread = 25;
const unsigned long max_threads = (length + min_per_thread -1) / min_per_thread;

const unsigned long hardware_threads = std::thread::hardware_concurrency();

const unsigned long num_threads = std::min(hardware_threads ? hardware_threads : 2 , max_threads);

const unsigned long block_size = length / num_threads;

vector<T> results(num_threads);
vector<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,std::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(&thread::join));

return std::accumulate(results.begin(),results.end(),init);
}

int main()
{
vector<int> myVec(100);

for(int i = 0; i < 100; ++i) myVec[i] = (i+1);

int ret = parallel_accumulate(myVec.begin(),myVec.end(),0);
cout<<"ret = "<<ret<<" \n";

return 0;

}

 

C++标准库为互斥量提供了一个RAII语法的模板类std::lock_guard,其会在构造的时候提供已锁的互斥量,并在析构的时候进行解锁。

当其中一个成员函数返回的是保护数据的指针或引用时,会破坏对数据的保护。具有访问能力的指针或者引用可以访问(并可能修改)被保护的数据,而不会被互斥锁限制。
切勿将受保护数据的指针或引用传递到互斥锁作用域之外,无论是函数返回值,还是存储在外部可见内存,亦或是以参数的形式传递到用户提供的函数中去。

#include <iostream>
#include <mutex>
#include <algorithm>
#include <list>

using namespace std;

list<int> some_list;
mutex some_mutex;

void add_to_list(int new_value)
{
lock_guard<mutex> guard(some_mutex);
some_list.push_back(new_value);
}

bool list_contains(int value_to_find)
{
lock_guard<mutex> guard(some_mutex);

return find(some_list.begin(),some_list.end(),value_to_find) != some_list.end();
}

int main()
{
return 0;
}

 

避免死锁的一般建议,就是让两个互斥量总是以相同的顺序上锁

#include <iostream>
#include <mutex>

using namespace std;

class some_big_object
{
};

void swap(some_big_object& lhs, some_big_object& rhs)
{
}

class X
{
private:
some_big_object some_detail;
mutex m;
public:
X(const some_big_object& sd) : some_detail(sd) {}

friend void swap(X& lhs, X& rhs)
{
if(&lhs == &rhs) return;

lock(lhs.m,rhs.m);
lock_guard<mutex> lock_a(lhs.m,std::adopt_lock);
lock_guard<mutex> lock_b(rhs.m,std::adopt_lock);

swap(lhs.some_detail,rhs.some_detail);
}
};
class X2
{
private:
some_big_object some_detail;
mutex m;
public:
X2(some_big_object& sd) : some_detail(sd) {}
friend void swap(X2& lhs, X2& rhs) {
if(&lhs == &rhs) return;

std::unique_lock<mutex> lock_a(lhs.m,std::defer_lock);
std::unique_lock<mutex> lock_b(rhs.m,std::defer_lock);

std::lock(lock_a,lock_b);
swap(lhs.some_detail,rhs.some_detail);
}
};

int main()
{
return 0;
}

 


避免嵌套锁: 当需要获取多个锁,使用一个std::lock来做这件事(对获取锁的操作上锁),避免产生死锁。
使用固定的顺序获取锁

层次锁的封装

#include <iostream>
#include <climits>
#include <mutex>
#include <thread>

using namespace std;

class hierarchical_mutex {
private:
mutex internal_mutex;

unsigned long const hierarchy_value;
unsigned long previous_hierarchy_value;

static thread_local unsigned long this_thread_hierarchy_value;

void check_for_hierarchy_violation()
{
if(this_thread_hierarchy_value <= hierarchy_value) {
throw std::logic_error("mutex hierarchy violated");
}
}

void update_hierarchy_value()
{
previous_hierarchy_value=this_thread_hierarchy_value;
this_thread_hierarchy_value = hierarchy_value;
}
public:
explicit hierarchical_mutex(unsigned long value) 
: hierarchy_value(value)
, previous_hierarchy_value(0)
{}

void lock()
{
check_for_hierarchy_violation();
internal_mutex.lock();
update_hierarchy_value();
}

void unlock()
{
this_thread_hierarchy_value=previous_hierarchy_value;
internal_mutex.unlock();
}

bool try_lock()
{
check_for_hierarchy_violation();
if(!internal_mutex.try_lock())
return false;
update_hierarchy_value();
return true;
}
};

thread_local unsigned long hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);


hierarchical_mutex high_level_mutex(10000);
hierarchical_mutex low_level_mutex(5000);

int do_low_level_stuff(){
cout<<__FUNCTION__<<"\n";
return 1;
}

int low_level_func()
{
lock_guard<hierarchical_mutex> lk(low_level_mutex);
return do_low_level_stuff();
}

void high_level_stuff(int some_param)
{
cout<<__FUNCTION__<<" "<<some_param<<"\n";
}

void high_level_func()
{
lock_guard<hierarchical_mutex> lk(high_level_mutex);
high_level_stuff(low_level_func());
}

void thread_a()
{
high_level_func();
}

hierarchical_mutex other_mutex(100);

void do_other_stuff()
{
cout<<__FUNCTION__<<"\n";
}

void other_stuff() 
{
high_level_func();
do_other_stuff();
}

void thread_t()
{
lock_guard<hierarchical_mutex> lk(other_mutex);
other_stuff();
}

 

int main()
{

thread t(thread_a);

t.join();

thread t2(thread_t);
t2.join();
return 0;
}

 

使用std::once_flag和std::call_once来实现对构造函数的保护

class X {
private:
connection_info connection_details;
connection_handle connection;
std::once_flag connection_init_flag;

void open_connection() {
connection = connection_manager.open(connection_details);
}

public:
X(connection_info const & connection_details_) :
connection_details(connection_details_) {}

void send_data(data_packet const & data) {
std::call_once(connection_init_flag, &X::open_connection,this);
connection.send_data(data);
}

data_packet receive_data() {
std::call_once(connection_init_flag, &X::open_connection,this);

return connection.receive_data();
}
};

 

C++的线程库中暂时没有读写锁,boost提供了读写锁

 

#include <map>
#include <string>
#include <mutex>
#include <boost/thread/shared_mutex.hpp>

class dns_entry;

class dns_cache {
private:
std::map<std::string, dns_entry> entries;
mutable boost::shared_mutex entry_mutex;

public:
dns_entry find_entry(std::string const & domain) const {
boost::shared_lock<boost::shared_mutex> lk(entry_mutex);

std::map<std::string,dns_entry>::const_iterator const it = entries.find(domain);

return (it == entries.end()) ? dns_entry() : it->second;
}

void update_or_add_entry(std::string const & domain, dns_entry const& dns_details) {

std::lock_guard<boost::shared_mutex> lk(entry_mutex);
entries[domain] = dns_details;
}
};

std::condition_variable 和 std::condition_variable_any 
<condition_variable>
两者都需要与一个互斥量一起才能工作
std::condition_variable和std::mutex一起工作
std::condition_variable_any 可以和任何满足最低标准的互斥量一起工作

 

转载于:https://www.cnblogs.com/bitsstitcher/p/11532512.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值