C++/MFC/ATL 线程安全字符串读/写
Posted
技术标签:
【中文标题】C++/MFC/ATL 线程安全字符串读/写【英文标题】:C++/MFC/ATL Thread-Safe String read/write 【发布时间】:2015-09-05 23:33:05 【问题描述】:我有一个启动线程的 MFC 类,线程需要修改主类的 CString 成员。
我讨厌互斥锁,所以必须有一种更简单的方法来做到这一点。
我正在考虑使用 boost.org 库或 atl::atomic 或 shared_ptr 变量。
读写字符串和线程安全的最佳方法是什么?
class MyClass
public:
void MyClass();
static UINT MyThread(LPVOID pArg);
CString m_strInfo;
;
void MyClass::MyClass()
AfxBeginThread(MyThread, this);
CString strTmp=m_strInfo; // this may cause crash
UINT MyClass::MyThread(LPVOID pArg)
MyClass pClass=(MyClass*)pArd;
pClass->m_strInfo=_T("New Value"); // non thread-safe change
根据 MSDN shared_ptr 自动工作 https://msdn.microsoft.com/en-us/library/bb982026.aspx
那么这是一个更好的方法吗?
#include <memory>
class MyClass
public:
void MyClass();
static UINT MyThread(LPVOID pArg);
std::shared_ptr<CString> m_strInfo; // ********
;
void MyClass::MyClass()
AfxBeginThread(MyThread, this);
CString strTmp=m_strInfo; // this may cause crash
UINT MyClass::MyThread(LPVOID pArg)
MyClass pClass=(MyClass*)pArd;
shared_ptr<CString> newValue(new CString());
newValue->SetString(_T("New Value"));
pClass->m_strInfo=newValue; // thread-safe change?
【问题讨论】:
使用 RAII 管理锁生命周期的关键部分。 【参考方案1】:您可以实现某种无锁方式来实现这一点,但这取决于您如何使用 MyClass 和您的线程。如果您的线程正在处理一些数据,并且在处理完之后,它需要更新 MyClass,然后考虑将您的字符串数据放在其他一些类中。:
struct StringData
CString m_strInfo;
;
然后在你的 MyClass 中:
class MyClass
public:
void MyClass();
static UINT MyThread(LPVOID pArg);
StringData* m_pstrData;
StringData* m_pstrDataForThreads;
;
现在,想法是在您的 ie 中。主线程代码你使用m_pstrData,但是你需要使用原子来存储指向它的本地指针。:
void MyClass::MyClass()
AfxBeginThread(MyThread, this);
StringData* m_pstrDataTemp = ATOMIC_READ(m_pstrData);
if ( m_pstrDataTemp )
CString strTmp=m_pstrDataTemp->m_strInfo; // this may NOT cause crash
一旦你的线程处理完数据,并且想要更新字符串,你将自动分配m_pstrDataForThreads
到m_pstrData
,并分配新的m_pstrDataForThreads
,
问题在于如何安全地删除m_pstrData,我想你可以在这里使用std::shared_ptr。
最后它看起来有点复杂,而且 IMO 并不真正值得付出努力,至少很难判断这是否真的是线程安全的,以及代码何时会变得更加复杂 - 它仍然是线程安全的。这也是针对单个工作线程的情况,你说你有多个线程。这就是为什么临界区是一个起点,如果它太慢,那么考虑使用无锁的方法。
顺便说一句。根据字符串数据的更新频率,您还可以考虑使用PostMessage
将指向新字符串的指针安全地传递给主线程。
[编辑]
ATOMIC_MACRO 不存在,它只是一个占位符,使其可以编译使用,即。 c++11 atomics,示例如下:
#include <atomic>
...
std::atomic<uint64_t> sharedValue(0);
sharedValue.store(123, std::memory_order_relaxed); // atomically store
uint64_t ret = sharedValue.load(std::memory_order_relaxed); // atomically read
std::cout << ret;
【讨论】:
我有 6 个线程,从 OnInitDialog 开始,然后当它们完成时,值会显示在对话框中。 但是你觉得 shared_ptr我会使用更简单的方法,用 SetStrInfo
保护变量:
void SetStrInfo(const CString& str)
[Lock-here]
m_strInfo = str;
[Unlock-here]
对于锁定和解锁,我们可以使用CCriticalSection
(类成员),或者将其包裹在CSingleLock
RAII 周围。出于性能原因,我们也可以使用slim-reader writer locks(使用 RAII 包装 - 编写一个简单的类)。我们还可以使用更新的 C++ 技术来锁定/解锁 RAII。
称我为老派,但对我来说,std
命名空间有一组复杂的选项 - 并不适合所有人,也不适合所有人。
【讨论】:
以上是关于C++/MFC/ATL 线程安全字符串读/写的主要内容,如果未能解决你的问题,请参考以下文章
使用Concurrent Dispatch Group进行线程安全读/写阵列访问[重复]