Проблемы с использованием EnterCriticalSection

Мне нужно работать с массивом из нескольких потоков, поэтому я использую 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' : cannot convert parameter 1 from 
'const CRITICAL_SECTION *' to 'LPCRITICAL_SECTION'

Я не знаю, что не так. Может быть, вы можете видеть. Просто потому, что я всегда использовал его таким образом, и все было в порядке. windows.h включено


person chester89    schedule 21.12.2008    source источник
comment
Не пишите шаблон ‹typename T›, а шаблон ‹typename T›   -  person pyon    schedule 22.12.2008


Ответы (6)


Просто объявите cs как:

mutable CRITICAL_SECTION cs;

или же удалить предложение const на size()

Вход в критический раздел изменяет CRITICAL_SECTION, а выход из него изменяет его снова. Поскольку вход в критическую секцию и выход из нее не делает вызов метода size() логически отличным от const, я бы посоветовал оставить его объявленным const и сделать cs mutable. Это тип ситуации, для которой был введен mutable.

Кроме того, взгляните на Мартина Йорка и Предложения Джо Муккиелло: по возможности используйте RAII для работы с любыми ресурсами, которые необходимо очистить. Это работает так же хорошо для критических секций, как и для указателей и файловых дескрипторов.

person Eclipse    schedule 21.12.2008

Кроме того, приведенный выше код не является безопасным для исключений.
Нет гарантии, что 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() позаботится об этом.

person Martin York    schedule 21.12.2008

Я предпочитаю использовать отдельный объект Acquisition вместо вашего кода. Ваш код ненадежен, когда возникает исключение между вызовами 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);
}
person jmucchiello    schedule 21.12.2008

EnterCriticalSection не принимает аргумент const. Кстати, это ошибка компиляции, а не ошибка компоновки...

Кроме того, вы уверены, что хотите передать критическую секцию в свой ctor, а затем заставить ctor выполнить вызов InitializeCriticalSection? Если вы хотите поделиться своим критическим разделом, я полагаю, вы сначала инициализируете его, а затем раздаете.

person Arnout    schedule 21.12.2008

Я вижу, вы объявили пустой конструктор копирования:

SharedVector(const SharedVector& rhs) {}

Я уверен, вы знаете, что эта функция ничего не делает и оставляет cs неинициализированным. Поскольку ваш класс содержит экземпляр CRITICAL_SECTION, вы должны обязательно запретить вызовы конструктора копирования и оператора присваивания, если вы не собираетесь полностью их реализовать. Вы можете сделать это, разместив следующие объявления в разделе private вашего класса:

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

Это предотвращает автоматическое создание компилятором неправильных версий этих методов, а также предотвращает их вызов в другом коде, который вы пишете (поскольку это только объявления, а не определения с {} кодовыми блоками).

Кроме того, как упоминал Arnout, конструктор, который принимает аргумент CRITICAL_SECTION&, кажется неправильным. Ваша реализация копирует переданный критический раздел в cs (что недопустимо для CRITICAL_SECTION), а затем вызывает InitializeCriticalSection(&cs), который перезаписывает копию, которую вы только что сделали, и создает новый критический раздел. раздел. Для вызывающего абонента, прошедшего через критическую секцию, это кажется неправильным, поскольку фактически игнорирует все, что было передано.

person Greg Hewgill    schedule 21.12.2008

Значит, что-то не так с правами доступа. Я сделал метод size() неконстантным, и теперь все в порядке.

person chester89    schedule 21.12.2008
comment
Выбранный ответ (используйте изменяемый) определенно лучше. - person Will Dean; 22.12.2008