C++多线程环境下的构造函数
多线程的环境里,我们总不可避免要使用锁。于是一个常见的场景就是:
class ObjectWithLock
{
private:
std::mutex mtx_;
SomeResType shared_res_; public:
// Constructor/Destructor
… void OpOnSharedRes()
{
std::lock_guard<std::mutex> lock(mtx_); // read/write shared resource: shared_res_
…
}
};
问题
多线程环境下该如何实现拷贝构造函数和移动构造函数呢?
要实现默认构造函数,没有挑战。使用成员初始化列表就可以:
ObjectWithLock() : mtx_(), shared_res_() {} // 如果SomeResType有默认构造函数的话。
那么我们的拷贝构造怎么写呢?
方案一
非常有可能见到的一种解决方案是:
ObjectWithLock(ObjectWithLock const & rhs)
{
std::lock_guard<std::mutex> lock(rhs.mtx_); shared_res_ = rhs.shared_res_;
}
优点
很明显,这个方案解决了我们的问题。
缺点
方案一的缺点也是显而易见的。就是shared_res_ = rhs.shared_res_。为什么这么说呢?
我们先来复习下成员变量初始化顺序。
C++成员变量的初始化顺序
成员变量的初始化顺序在C++里弄不好,就是各种坑。
首先我们比较容易知道的是,C++成员变量的初始化发生在构造函数之前。
那么成员变量之间初始化的顺序是如何的呢?
没有专门去注意过这个问题的童鞋,很可能会认为,成员变量的初始化顺序是有成员变量初始化列表的顺序决定的。而事实上成员变量的初始化顺序是有其声明顺序决定的。
方案一的问题
所以方案一的问题就是,shared_res_ = rhs.shared_res_,做的是赋值而不是初始化。如果我们要过分看待这个问题的话,那么结论就是这会带来性能损耗(看具体情况,对性能存在很大的影响也是有可能的)。
当然很多时候我们没有必要吹毛求疵。事实上方案一最大的问题是他有很多局限性。这些局限性要求SomeResType必须:
- 有无参默认构造
- 支持赋值操作
- 没有const成员变量,没有引用类型的成员变量等
如果SomeResType不支持这些前提条件,那么为了解决当前的问题,要么让SomeResType支持,要么重新设计ObjectWithLock。如果ObjectWithLock是一个正确的设计想法,那么就只能为难SomeResType了。蛋疼。
难道这个问题就无解了不成?当然不是这样的,不过如果按照C++98/03的标准来做,真的不容易。我们还是来看看C++11能给我们带来什么惊喜吧。
方案二
C++11引入了一个和构造函数相关的语法,delegate constructor,代理构造函数。一个在C#里早就存在的语法,终于C++里也有了。
所谓的delegate constructor,就是构造函数的初始化可以代理(delegates to)给该类另外一个构造函数去完成。一个简单的例子(来自C++11标准文档12.6.2,段落6)就是:
struct C
{
C(int c) {} // #1: non-delegating constructor
C() : C() {} // #2: delegate to #1
};
有了代理构造函数,我们的问题就容易解决了。我们要解决的问题的关键就是,在初始化成员变量前,先将rhs对象lock住。
那么原先的拷贝构造函数要做到和下面伪代码类似的事情:
ObjectWithLock::ObjectWithLock(ObjectWithLock const & rhs)
: shared_res_(rhs.shared_res_), lock(rhs)
{}
借助代理构造函数,我们先实现一个自定义的构造函数,能够做拷贝,并且接受一把锁:
ObjectWithLock::ObjectWithLock(ObjectWithLock const & rhs,
std::lock_guard<std::mutex> const &)
: shared_res_(rhs.shared_res_)
{}
然后很自然我就有了:
ObjectWithLock::ObjectWithLock(ObjectWithLock const & rhs)
ObjctWithLock(rhs, std::lock_guard<std::mutex>(rhs.mtx_))
{}
问题完美解决了!
C++98/03的解决方案
C++98/03到底能不能解决这个问题呢?能!当然能!就是看你能不能想到了。
这里我就直接给结果,留给大家自己去想吧。
ObjectWithLock::ObjectWithLock(ObjectWithLock const & rhs)
: shared_res_((std::lock_guard<std::mutex>(rhs.mtx_), rhs.shared_res_))
{}
另外,这里ObjectWithLock只有一个资源shared_res_,如果有多个资源,解决方案类似,但是需要将多个资源先封装到一个结构中,然后将该结构应用到ObjectWithLock类中。
结束语
这里就只罗列的拷贝构造函数,移动构造函数同理,留给大家自己想吧。完!
C++多线程环境下的构造函数的更多相关文章
- Asp.Net在多线程环境下的状态存储问题
在应用开发中,我们经常需要设置一些上下文(Context)信息,这些上下文信息一般基于当前的会话(Session),比如当前登录用户的个人信息:或者基于当前方法调用栈,比如在同一个调用中涉及的多个层次 ...
- Java指令重排序在多线程环境下的应对策略
一.序言 指令重排在单线程环境下有利于提高程序的执行效率,不会对程序产生负面影响:在多线程环境下,指令重排会给程序带来意想不到的错误. 本文对多线程指令重排问题进行复原,并针对指令重排给出相应的解决方 ...
- C#多线程环境下调用 HttpWebRequest 并发连接限制
C#多线程环境下调用 HttpWebRequest 并发连接限制 .net 的 HttpWebRequest 或者 WebClient 在多线程情况下存在并发连接限制,这个限制在桌面操作系统如 win ...
- SQLite在多线程环境下的应用
文一 SQLite的FAQ里面已经专门说明,先贴出来.供以后像我目前的入门者学习. (7) 多个应用程序或者同一个应用程序的多个例程能同时存取同一个数据库文件吗? 多进程可以同时打开同一个数据库,也可 ...
- 多线程环境下非安全Dictionary引起的“已添加了具有相同键的项”问题
问题: 代码是在多线程环境下,做了简单的Key是否存的判断, 测试代码如下: public class Program { static Dictionary<string, Logger> ...
- HttpClient在多线程环境下踩坑总结
问题现场 在多线程环境下使用HttpClient组件对某个HTTP服务发起请求,运行一段时间之后发现客户端主机CPU利用率呈现出下降趋势,而不是一个稳定的状态. 而且,从程序日志中判断有线程处于han ...
- 多线程环境下的UI异步操作
转自原文 多线程环境下的UI异步操作 解决VS中,线程间不可互操作的问题,一揽子解决方案: 一.首先,定义一个类:SetControlProperty using System.Reflection; ...
- 单例模式在多线程环境下的lazy模式为什么要加两个if(instance==null)
刚才在看阿寻的博客”C#设计模式学习笔记-单例模式“时,发现了评论里有几个人在问单例模式在多线程环境下为什么lazy模式要加两个if进行判断,评论中的一个哥们剑过不留痕,给他们写了一个demo来告诉他 ...
- 一个由单例模式在多线程环境下引发的 bug
问题症状 HTTP 日志系统,老是出现日志信息覆盖的情况.比如同时调用 A 接口和 B 接口,B 接口请求响应信息变成了 A 接口请求响应相关信息.这个问题在并发量大的情况下越来越严重. 问题初步分析 ...
随机推荐
- (十二)select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET
select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型:int select(int maxfd,fd_set *rdset ...
- linux diff命令
diff 命令是 linux上非常重要的工具,用于比较文件的内容,特别是比较两个版本不同的文件以找到改动的地方.diff在命令行中打印每一个行的改动.最新版本的diff还支持二进制文件.diff程序的 ...
- string 常量池的理解
1: String a="123"; String b="12"+"3"; String c="1"+"23& ...
- SSH框架搭建
1.建项目 2.添加Struts 3.添加String 注意:这里一共是五个包 4.添加Hibernate 添加完成,然后导入数据库 这时候是这样的 把小绿叶放到WEB-INF 5.简单写struts ...
- JS学习之事件流
本文是原创文章,如需转载,请注明文章出处 JS和AS师出同源,所以事件流的3个阶段也跟AS一样,分别是捕获阶段,目标阶段,冒泡阶段. 下面写了个例子可以清晰的看出事件流的顺序: eventTest.h ...
- Java 中的 request 和response 区别
1.response 属于重定向请求: 其地址栏的URL会改变: 会向服务器发送两次请求: 2. request 属于请求转发: 其地址栏的URL不会改变: 向服务器发送一次请求: 举一个区分它们的简 ...
- 从客户端(Content="<p>测试</p>")中检测到有潜在危险的 Request.Form 值
.NetFrameWork 4.0 Validaterequest="false"不起作用 要恢复到2.0的ASP.NET请求验证功能的行为,要在以下设置 Web.config中 ...
- 奇淫绝技:Mysql报错注入利用总结分享
http://xxx.cn/qcwh/content/detail.php?id=330&sid=19&cid=261 and exists(select*from (select*f ...
- Python:函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 定义和调用 >>> def add(x,y): ... print('x=',x) #Python3必须加&quo ...
- CoreOS Linux引入了Kubernetes kubelet
CoreOS Linux引入了Kubernetes kubelet 作者:Kelsey Hightower 2015年8月14日 这周我们在 CoreOS Linux 的 alpha 开发版集成了 k ...