Потокобезопасная реализация стека - PullRequest
0 голосов
/ 10 июня 2019

Чтение

Энтони Уильямс "Параллелизм C ++ в действии" (1-е издание)

вы можете найти эту реализацию для многопоточного стека:

struct empty_stack: std::exception
{
    const char* what() const throw()
    {
        return "empty stack";
    }
};  

template<typename T>  
class threadsafe_stack  
{  
    private:
    std::stack<T> data;
    mutable std::mutex m;  
    public:
    threadsafe_stack(){}
    threadsafe_stack(const threadsafe_stack& other)
    {
        std::lock_guard<std::mutex> lock(other.m);
        data=other.data;
    }
    threadsafe_stack& operator=(const threadsafe_stack&) = delete;

    void push(T new_value)
    {
        std::lock_guard<std::mutex> lock(m);
        data.push(new_value);
    }
    std::shared_ptr<T> pop()
    {
        std::lock_guard<std::mutex> lock(m);
        if(data.empty()) throw empty_stack();
        std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
        data.pop();
        return res;
    }
    void pop(T& value)
    {
        std::lock_guard<std::mutex> lock(m);
        if(data.empty()) throw empty_stack();
        value=data.top();
        data.pop();
    }
    bool empty() const
    {
        std::lock_guard<std::mutex> lock(m);
        return data.empty();
    }
};  

Итак, обсуждая возможные реализации, автор предлагает следующую версию для pop ():

void pop(T& value)  
{  
    std::lock_guard<std::mutex> lock(m);  
    if(data.empty()) throw empty_stack();  
    value=data.top();  
    data.pop();  
}  

и подчеркивает некоторые предостережения, и одно из этих предостережений заключается в том, что это решение требует, чтобы тип, хранящийся в стеке, был назначаемым.

В то же время он показывает следующий пример использования:

std::vector<int> result;
some_stack.pop(result);

Теперь мое возражение:
по моему мнению, еще одна оговорка:
оператор присваивания типа, хранящегося в стеке безопасного потока, не должен генерировать исключение, как оператор присваивания std :: vector.

Основано ли мое возражение?

Причины моего возражения:
оно вытекает из анализа следующего (не реализованного) решения, предложенного автором:

"вы можете написать возвращающее значение pop (), если возвращаемое по значению не может вызвать исключение, поэтому для типов, хранящихся в стеке, безопасном для потока, требуется не копирующий конструктор копирования или конструктор перемещения" .

Спасибо за ваше время.

1 Ответ

1 голос
/ 12 июня 2019

Почему вы чувствуете, что можно бросить empty_stack, но не ОК, чтобы бросить то, что может дать векторное назначение?

Проблема с T pop() заключается в том, что ему нужно сначала удалить значение из стека, а затем скопировать его - что может привести к потере, и в этот момент вы потеряете значение; он не возвращается и не остается в контейнере. Другими словами, такой дизайн не может обеспечить строгую гарантию исключения .

Именно поэтому std::stack предоставляет два отдельных вызова - T top() для чтения верха без изменения стека (если он выбрасывает, стек остается без изменений), и void pop(), который изменяет стек и гарантированно не будет бросить.

...