我正在考虑如何实现一个包含私有数据的类,这些私有数据最终会被多个线程通过方法调用修改。为了同步(使用Windows API),我计划使用CRITICAL_SECTION
对象,因为所有线程都将从同一进程中生成。
鉴于以下设计,我有几个问题。
template <typename T> class Shareable
{
private:
const LPCRITICAL_SECTION sync; //Can be read and used by multiple threads
T *data;
public:
Shareable(LPCRITICAL_SECTION cs, unsigned elems) : sync{cs}, data{new T[elems]} { }
~Shareable() { delete[] data; }
void sharedModify(unsigned index, T &datum) //<-- Can this be validly called
//by multiple threads with synchronization being implicit?
{
EnterCriticalSection(sync);
/*
The critical section of code involving reads & writes to 'data'
*/
LeaveCriticalSection(sync);
}
};
// Somewhere else ...
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
Shareable<ActualType> *ptr = static_cast<Shareable<ActualType>*>(lpParameter);
T copyable = /* initialization */;
ptr->sharedModify(validIndex, copyable); //<-- OK, synchronized?
return 0;
}
我看到它的方式,API调用将在当前线程的上下文中进行。也就是说,我认为这与从指针获取临界区对象并从ThreadProc()
内调用API一样。但是,我担心如果创建对象并将其放在主/初始线程中,那么API调用会有一些时髦。
sharedModify()
同时在同一个对象上调用时,
从多个线程,将同步隐含,在
我在上面描述过的方式?答案 0 :(得分:2)
当同一个对象同时从多个线程调用sharedModify()时,同步是隐式的,就像我上面描述的那样吗?
它不是隐含的,它是明确的。只有CRITICAL_SECTION
只有一个线程可以同时保存它。
我是否应该获取指向临界区对象的指针并使用它?
没有。这里没有理由使用指针。
是否有其他同步机制更适合这种情况?
如果没有看到更多代码,很难说,但这绝对是&#34;默认&#34;解。它就像一个单链表 - 你首先学习它,它总是有效的,但它并不总是最好的选择。
答案 1 :(得分:1)
当
sharedModify()
同时在同一个对象上调用时,从多个线程中,同步是隐式的,就像我上面描述的那样?
从来电者的角度来看是隐含的,是的。
我是否应该获取指向临界区对象的指针并使用它?
没有。事实上,我建议给Sharable
对象拥有自己的关键部分,而不是从外部接受一个(并使用RAII概念来编写更安全的代码),例如:
template <typename T>
class Shareable
{
private:
CRITICAL_SECTION sync;
std::vector<T> data;
struct SyncLocker
{
CRITICAL_SECTION &sync;
SyncLocker(CRITICAL_SECTION &cs) : sync(cs) { EnterCriticalSection(&sync); }
~SyncLocker() { LeaveCriticalSection(&sync); }
}
public:
Shareable(unsigned elems) : data(elems)
{
InitializeCriticalSection(&sync);
}
Shareable(const Shareable&) = delete;
Shareable(Shareable&&) = delete;
~Shareable()
{
{
SyncLocker lock(sync);
data.clear();
}
DeleteCriticalSection(&sync);
}
void sharedModify(unsigned index, const T &datum)
{
SyncLocker lock(sync);
data[index] = datum;
}
Shareable& operator=(const Shareable&) = delete;
Shareable& operator=(Shareable&&) = delete;
};
是否有其他同步机制更适合这种情况?
这取决于。多个线程是否会同时访问相同的索引?如果没有,那么根本不需要关键部分。一个线程可以安全地访问一个索引,而另一个线程访问不同的索引。
如果多个线程需要同时访问同一个索引,那么关键部分可能仍然不是最佳选择。如果您只需要一次锁定阵列的部分,则锁定整个阵列可能是一个很大的瓶颈。诸如Interlocked API或Slim Read / Write锁之类的东西可能更有意义。这实际上取决于你的线程设计以及你实际想要保护的内容。