构建一个 C++对象(MFC CRecordset)线程安全
我们正在尝试构建一个提供 MFC 的类CRecordset (或者实际上是 CODBCRecordset 类)线程安全。实际上,对于各种功能,例如打开和移动记录集(我们将这些调用包含在关键部分中),一切似乎都运行得很好,但是,仍然存在一个问题,这个问题似乎在实践中引入了死锁。
问题似乎出在我们的构造函数中,如下所示:
CThreadSafeRecordset::CThreadSafeRecordset(void) : CODBCRecordset(g_db)
{ // <-- Deadlock!
}
尽管我们保护了封闭的 Close 调用,但另一个线程可能最终进入了 CThreadSafeRecordset::Close() ,但这并不重要,因为构造函数是在不知情的情况下进行线程处理的。我认为原来的 CRecordset 类是罪魁祸首,在构建时做了坏事。我已经四处寻找编程技术来解决这个问题,但我不确定什么是最好的解决方案?由于我们没有代码并且无法控制构造函数中的其他代码,因此我们无法在关键部分中包装任何特殊内容......?
更新:感谢您的意见;我已将最终得到的结果标记为问题的答案。结合返回 shared_ptr 作为返回实例以便于更新现有的线程不感知代码。
We're trying to build a class that provides the MFC CRecordset (or, really, the CODBCRecordset class) thread safety. Everything actually seem to work out pretty well for the various functions like opening and moving through the recordset (we enclose these calls with critical sections), however, one problem remains, a problem that seems to introduce deadlocks in practice.
The problem seems to lie in our constructor, like this:
CThreadSafeRecordset::CThreadSafeRecordset(void) : CODBCRecordset(g_db)
{ // <-- Deadlock!
}
The other thread might be one having ended up in CThreadSafeRecordset::Close() despite us guarding the enclosed Close call, but that doesn't really matter since the constructor is threading unaware. I assume the original CRecordset class is the culprit, doing bad things at construction time. I've looked around for programming techniques to work around this problem, but I'm unsure what could be the best solution? Since we have no code and can't control other code in our constructor, we can't wrap anything special in a critical section...?
Update: Thanks for the input; I've marked what I ended up with as the answer to my question. That, in combination with returning a shared_ptr as the returned instance for ease of updating the existing thread-unaware code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以将
CThreadSafeRecordset
构造函数设为私有,然后提供一个公共工厂方法来参与锁定并返回一个实例。You can make the
CThreadSafeRecordset
constructor private, then provide a public factory method that participates in your locking and returns an instance.如果无法使 CODBCRecordset 将其线程不安全操作移出构造函数(例如,默认构造函数后跟 Initialize() 调用),则始终可以使用组合而不是继承。让 CThreadSafeRecordset 成为 CODBCRecordset 的包装器而不是它的子类。这样,您就可以随时显式地构建您的记录集,并可以以适当的严格程度来保护它。
当然,缺点是您必须包装您希望公开的每个 CODBCRecordset 方法,甚至是那些与线程保证无关的方法。 cpp 文件中的 AC 宏(这样它就无法逃脱并困扰您的客户)可能会有所帮助。
If there's no way to make CODBCRecordset move its thread-unsafe operations out of the constructor (a default constructor followed by an Initialize() call, say), you can always use composition instead of inheritance. Let CThreadSafeRecordset be a wrapper around CODBCRecordset instead of a subclass of it. That way, you can explicitly construct your recordset whenever you like, and can defend it with whatever rigor is appropriate.
The drawback, of course, is that you'll have to wrap every CODBCRecordset method you wish to expose, even the ones that don't relate to your threading guarantees. A C macro in the cpp file (so that it can't escape and afflict your clients) may help.