同步方法从多个线程调用共享对象

时间:2016-11-29 22:22:36

标签: c++ multithreading winapi

我正在考虑如何实现一个包含私有数据的类,这些私有数据最终会被多个线程通过方法调用修改。为了同步(使用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调用会有一些时髦。

  1. sharedModify()同时在同一个对象上调用时, 从多个线程,将同步隐含,在 我在上面描述过的方式?
  2. 我应该找一个指针 关键部分对象并使用它而不是?
  3. 还有其他的吗? 更适合这种情况的同步机制?

2 个答案:

答案 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锁之类的东西可能更有意义。这实际上取决于你的线程设计以及你实际想要保护的内容。