Проблеми при използване на 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 е въведена.

Също така – разгледайте Мартин Йорк и Предложенията на Joe Mucchiello - използвайте 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

Предпочитам да използвам отделен обект за придобиване пред вашия код. Вашият код, ако е крехък, когато възникне изключение между извикванията 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. Това е грешка при компилиране, BTW, а не грешка при свързване...

Също така, сигурни ли сте, че искате да предадете критична секция във вашия 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() non-const и сега е добре.

person chester89    schedule 21.12.2008
comment
Избраният отговор (използвайте променлив) определено е по-добър. - person Will Dean; 22.12.2008