使用EnterCriticalSection时出现的问题

7

我需要在多个线程中操作数组,因此我使用CRITICAL SECTION来独占地访问数据。
这是我的模板:

#include "stdafx.h"
#ifndef SHAREDVECTOR_H
#define SHAREDVECTOR_H

#include <vector>
#include <windows.h>

template<class T>
class SharedVector {
    std::vector<T> vect;
    CRITICAL_SECTION cs;
    SharedVector(const SharedVector<T>& rhs) {}
public:
    SharedVector();
    explicit SharedVector(const CRITICAL_SECTION& CS);
    void PushBack(const T& value);
    void PopBack();
    unsigned int size() const;
    T& operator[](int index);
    virtual ~SharedVector();
};

template<class T>
SharedVector<T>::SharedVector() {
    InitializeCriticalSection(&cs);
}

template<class T>
SharedVector<T>::SharedVector(const CRITICAL_SECTION& r): cs(r) {
    InitializeCriticalSection(&cs);
}

template<class T>
void SharedVector<T>::PushBack(const T& value) {
    EnterCriticalSection(&cs);
    vect.push_back(value);
    LeaveCriticalSection(&cs);
}

template<class T>
void SharedVector<T>::PopBack() {
    EnterCriticalSection(&cs);
    vect.pop_back();
    LeaveCriticalSection(&cs);
}

template<class T>
unsigned int SharedVector<T>::size() const {
    EnterCriticalSection(&cs);
    unsigned int result = vect.size();
    LeaveCriticalSection(&cs);
    return result;
}

template<class T>
T& SharedVector<T>::operator[](int index) {
    EnterCriticalSection(&cs);
    T result = vect[index];
    LeaveCriticalSection(&cs);
    return result;
}

template<class T>
SharedVector<T>::~SharedVector() {
    DeleteCriticalSection(&cs);
}

在编译时,我在调用 EnterCriticalSection(&cs)LeaveCriticalSection(&cs) 时遇到了这样的问题:

'EnterCriticalSection':无法将参数 1 从 'const CRITICAL_SECTION *' 转换为 'LPCRITICAL_SECTION'

我不知道出了什么问题。也许你能看出来。因为我总是以这种方式使用它,并且一切都正常。已经包含了windows.h


不要写成 template <typename T>,而是应该写成 template<typename T>。 - isekaijin
6个回答

21

只需将cs声明为:

mutable CRITICAL_SECTION cs;

否则,请移除size()上的const限定符。
进入临界区修改CRITICAL_SECTION,离开后再次修改。由于进入和离开临界区并不使size()方法调用在逻辑上变得非const,因此我建议保留其声明为const,并将cs设置为mutable。这是mutable引入的情况之一。
另外,请看看Martin YorkJoe Mucchiello的建议 - 尽可能使用RAII来处理需要清理的任何资源。对于临界区、指针和文件句柄,这同样适用。

8

此外,上述代码并不是异常安全的。
不能保证 push_back() 和 pop_back() 不会抛出异常。如果它们抛出异常,将导致关键部分永久锁定。您应该创建一个锁定类,在构造函数中调用 EnterCriticalSection(),在析构函数中调用 LeaveCriticalSection()。

同时,这样可以使您的方法更易于阅读(见下文)。

class CriticalSectionLock
{
    public:
        CriticalSectionLock(CRITICAL_SECTION& cs)
            : criticalSection(cs)
        {
            EnterCriticalSection(&criticalSection);
        }
        ~CriticalSectionLock()
        {
            LeaveCriticalSection(&criticalSection);
        }
    private:
        CRITICAL_SECTION&  criticalSection;
};


// Usage
template
unsigned int SharedVector::size() const
{
    CriticalSectionLock  lock(cs);
    return vect.size();
}

还有一件事情你需要担心,就是确保在销毁对象时你拥有它,并且在销毁过程中没有其他人试图接管它的所有权。希望你的DestoryCriticalSection()函数能够处理这个问题。


3

我更喜欢使用单独的获取对象而不是你的代码。在Enter和Leave调用之间发生异常时,你的代码很容易出现问题:

class CS_Acquire {
    CRITICAL_SECTION &cs;
public:
    CS_Acquire(CRITICAL_SECTION& _cs) : cs(_cs) { EnterCriticalSection(cs); }
    ~CS_Acquire() { LeaveCriticalSection(cs); }
};

然后在你的类方法中,你需要编写以下代码:

template <typename T>
void SharedVector::PushBack(const T& value) {
   CS_Acquire acquire(&cs);
   vect.push_back(value);
}

1

EnterCriticalSection 不接受 const 参数。这是编译错误,顺便说一下,不是链接错误...

此外,您确定要将关键部分传递到构造函数中,然后让构造函数执行 InitializeCriticalSection 调用吗?如果您想共享关键部分,我想您应该先初始化它,然后再分发。


1

我看到你声明了一个空的复制构造函数:

SharedVector(const SharedVector& rhs) {}

正如您所知,此函数无作用,并且也未初始化cs。由于您的类包含CRITICAL_SECTION 的实例,因此必须确保禁止复制构造函数和赋值运算符调用,除非您要完全实现它们。 您可以通过将以下声明放置在类的private部分来实现此目的:

SharedVector(const SharedVector &);
SharedVector &operator=(const SharedVector &);

这样可以防止编译器自动生成这些方法的不正确版本,也可以防止您在编写的其他代码中调用它们(因为这些只是声明,而不是带有{}代码块的定义)。

此外,正如Arnout所提到的,接受CRITICAL_SECTION&参数的构造函数似乎是错误的。实现所做的是将传入的临界区域复制到cs(这对于CRITICAL_SECTION来说不是有效的事情),然后调用InitializeCriticalSection(&cs),它会覆盖刚刚进行的复制并创建一个新的临界区域。对于传递临界区域的调用者而言,这似乎做了错误的事情,从根本上忽略了传递的任何内容。


0

所以,访问权限出了问题。 我将size()方法改为非const,现在没问题了。


所选答案(使用mutable)绝对更好。 - Will Dean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接