从多个线程同步对共享对象的方法调用
Posted
技术标签:
【中文标题】从多个线程同步对共享对象的方法调用【英文标题】:Synchronizing method calls on shared object from multiple threads 【发布时间】:2016-11-29 22:22:36 【问题描述】:我正在考虑如何实现一个包含私有数据的类,这些私有数据最终将由多个线程通过方法调用进行修改。对于同步(使用 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) : synccs, datanew 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()
同时在同一个对象上调用时,
从多个线程中,同步是否是隐式的,在
我上面描述的方式?
我是否应该获得指向
临界区对象并改用它?
还有其他的吗
更适合这种情况的同步机制?
【问题讨论】:
【参考方案1】:当从多个线程同时对同一个对象调用 sharedModify() 时,同步是否会像我上面描述的那样是隐式的?
这不是隐含的,而是明确的。只有CRITICAL_SECTION
,而且一次只能持有一个线程。
我应该获取一个指向临界区对象的指针并使用它吗?
没有。这里没有理由使用指针。
还有其他更适合这种情况的同步机制吗?
不看更多代码很难说,但这绝对是“默认”解决方案。它就像一个单链表——你先学会它,它总是有效的,但它并不总是最好的选择。
【讨论】:
【参考方案2】:当
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;
;
还有其他更适合这种情况的同步机制吗?
这取决于。多个线程会同时访问 same 索引吗?如果没有,那么根本不需要关键部分。一个线程可以安全地访问一个索引,而另一个线程访问另一个索引。
如果多个线程需要同时访问同一个索引,临界区可能仍然不是最佳选择。如果您一次只需要锁定数组的部分,那么锁定整个 数组可能是一个很大的瓶颈。诸如 Interlocked API 或 Slim Read/Write 锁之类的东西可能更有意义。这实际上取决于您的线程设计以及您实际尝试保护的内容。
【讨论】:
以上是关于从多个线程同步对共享对象的方法调用的主要内容,如果未能解决你的问题,请参考以下文章