Потокобезопасный интерфейс
#новичкам
Не для всех очевидная новость: не всегда можно превратить класс из небезопасного в потокобезопасный, просто по уши обложившись лок гардами. Да, вызов конкретного метода будет безопасен. Но это не значит, что классом безопасно пользоваться.
Возьмем максимально простую реализацию самой простой очереди:
Она конечно потокоНЕбезопасная. То есть ей можно адекватно пользоваться только в рамках одного потока.
Как может выглядеть код простого консьюмера этой очереди?
И вот мы захотели разделить обязанности производителя чисел и их потребителя между разными потокам. Значит, нам надо как-то защищать очередь от многопоточных неприятностей.
Бабахаем везде лок гард на один мьютекс и дело в шляпе!
Все доступы к очереди защищены. Но спасло ли реально это нас?
Вернемся к коду консюмера:
А вдруг у нас появится еще один консюмер? Тогда в первом из них мы можем войти условие, а в это время второй достанет последний элемент. Получается, что мы получим доступ к неинициализированной памяти в методе front.
То есть по факту в многопоточном приложении полученный стейт сущности сразу же утрачивает свою актуальность.
Что делать? Не только сами методы класса должны быть потокобезопасными. Но еще и комбинации использования этих методов тоже должны обладать таким свойством. И с данным интерфейсом это сделать просто невозможно.
Если стейт утрачивает актуальность, то мы вообще не должны давать возможность приложению получать стейт очереди. Нам нужны только команды управления. То есть push и pop.
Внутри метода
Теперь консюмер выглядит так:
Можно конечно было использовать кондвары и прочее. Но я хотел сфокусироваться именно на интерфейсе. Теперь реализация просто не позволяет получать пользователю потенциально неактульные данные.
Stay safe. Stay cool.
#concurrency #design #goodpractice
#новичкам
Не для всех очевидная новость: не всегда можно превратить класс из небезопасного в потокобезопасный, просто по уши обложившись лок гардами. Да, вызов конкретного метода будет безопасен. Но это не значит, что классом безопасно пользоваться.
Возьмем максимально простую реализацию самой простой очереди:
struct Queue {
void push(int value) {
storage.push_back(value);
}
void pop() {
storage.pop_front();
}
bool empty() {
return storage.empty();
}
int& front() {
return storage.front();
}
private:
std::deque<int> storage;
};
Она конечно потокоНЕбезопасная. То есть ей можно адекватно пользоваться только в рамках одного потока.
Как может выглядеть код простого консьюмера этой очереди?
while(condition)
if (!queue.empty()) {
auto & elem = queue.front();
process_elem(elem);
queue.pop();
}
И вот мы захотели разделить обязанности производителя чисел и их потребителя между разными потокам. Значит, нам надо как-то защищать очередь от многопоточных неприятностей.
Бабахаем везде лок гард на один мьютекс и дело в шляпе!
struct Queue {
void push(int value) {
std::lock_guard lg{m};
storage.push_back(value);
}
void pop() {
std::lock_guard lg{m};
storage.pop_front();
}
bool empty() {
std::lock_guard lg{m};
return storage.empty();
}
int& front() {
std::lock_guard lg{m};
return storage.front();
}
private:
std::deque<int> storage;
std::mutex m;
};
Все доступы к очереди защищены. Но спасло ли реально это нас?
Вернемся к коду консюмера:
while(true)
if (!queue.empty()) {
auto & elem = queue.front();
process_elem(elem);
queue.pop();
}
А вдруг у нас появится еще один консюмер? Тогда в первом из них мы можем войти условие, а в это время второй достанет последний элемент. Получается, что мы получим доступ к неинициализированной памяти в методе front.
То есть по факту в многопоточном приложении полученный стейт сущности сразу же утрачивает свою актуальность.
Что делать? Не только сами методы класса должны быть потокобезопасными. Но еще и комбинации использования этих методов тоже должны обладать таким свойством. И с данным интерфейсом это сделать просто невозможно.
Если стейт утрачивает актуальность, то мы вообще не должны давать возможность приложению получать стейт очереди. Нам нужны только команды управления. То есть push и pop.
struct ThreadSafeQueue {
void push(int value) {
std::lock_guard lg{m};
storage.push_back(value);
}
std::optional<int> pop() {
std::lock_guard lg{m};
if (!storage.empty()) {
int elem = storage.front();
storage.pop_front();
return elem;
}
return nullopt;
}
private:
std::deque<int> storage;
std::mutex m;
};
Внутри метода
pop
мы можем использовать проверять и получать стейт очереди, так как мы оградились локом. Возвращаем из него std::optional
, который будет хранить фронтальный элемент, если очередь была непуста. В обратном случае он будет пуст.Теперь консюмер выглядит так:
while(true) {
auto elem = queue.pop();
if (elem)
process_elem(elem.value());
}
Можно конечно было использовать кондвары и прочее. Но я хотел сфокусироваться именно на интерфейсе. Теперь реализация просто не позволяет получать пользователю потенциально неактульные данные.
Stay safe. Stay cool.
#concurrency #design #goodpractice
❤1
👩💻 Вспоминаем методы для контейнеров!
В этом посте мы рассмотрим функции и методы для контейнеров. Они позволяют оптимизировать код, сокращая количество операций, и делают работу с контейнерами более удобной.
Уютное сообщество С++ разработчиков | #шпора
В этом посте мы рассмотрим функции и методы для контейнеров. Они позволяют оптимизировать код, сокращая количество операций, и делают работу с контейнерами более удобной.
Уютное сообщество С++ разработчиков | #шпора
🔥3❤1
This media is not supported in your browser
VIEW IN TELEGRAM
Сортировка массива на C++ за 30 секунд!
👍5👌1
Считаем единички
#задачки
Меня всегда забавляли задачки на бинарное представление чисел. Всегда можно найти занимательные и новые для себя подходы.
Вроде бы простая и популярная задача: посчитать количество единиц в битовом представлении числа. Уверен, что большинство из вас решали эту задачу.
Однако популярный подход - не самый эффективный, элегантный и интересный.
Благо существует множество непопулярных, но очень интересных решений! О них я расскажу в завтра в ответном посте.
А пока предлагаю вам порешать эту задачку. Если вы знаете какие-то нетривиальные решения, то расскажите о них в комментах тоже.
А чтобы умудренным опытом людям было немного интереснее, давайте ограничим условия. Задачу надо решить либо за константное время(в среднем), либо за наименьшее количество строчек. Выражения вне цикла for разделенные символом
Challenge yourself. Stay cool.
#задачки
Меня всегда забавляли задачки на бинарное представление чисел. Всегда можно найти занимательные и новые для себя подходы.
Вроде бы простая и популярная задача: посчитать количество единиц в битовом представлении числа. Уверен, что большинство из вас решали эту задачу.
Однако популярный подход - не самый эффективный, элегантный и интересный.
Благо существует множество непопулярных, но очень интересных решений! О них я расскажу в завтра в ответном посте.
А пока предлагаю вам порешать эту задачку. Если вы знаете какие-то нетривиальные решения, то расскажите о них в комментах тоже.
А чтобы умудренным опытом людям было немного интереснее, давайте ограничим условия. Задачу надо решить либо за константное время(в среднем), либо за наименьшее количество строчек. Выражения вне цикла for разделенные символом
;
считаются разными строчками.int count_ones(unsigned num) {
// Here your code
}
Challenge yourself. Stay cool.
❤1
🎲 Тест «Тест по C++»
Пройдите тестирование, проверьте свои знания с помощью онлайн тест-викторины C++, подготовьтесь к экзаменам по C++.
🖊 15 вопросов · ⏱ 30 сек
Пройдите тестирование, проверьте свои знания с помощью онлайн тест-викторины C++, подготовьтесь к экзаменам по C++.
🖊 15 вопросов · ⏱ 30 сек
👍1🥱1
📚 Основное определение:
Поинтеры — это переменные, которые хранят адреса других переменных в памяти. Они активно используются в системном программировании, разработке встраиваемых систем и при работе с динамической памятью. Главная особенность — прямой доступ к памяти и эффективная работа с данными.
🔍 Ключевые концепции:
1️⃣ Объявление поинтера: int *ptr — создает указатель на целое число
2️⃣ Получение адреса: &variable — оператор для получения адреса переменной
3️⃣ Разыменование: *ptr — доступ к значению по адресу в указателе
4️⃣ Арифметика указателей: ptr++ перемещает указатель на следующий элемент
5️⃣ Связь с массивами: array[i] эквивалентно *(array + i)
6️⃣ Динамическая память: malloc() и free() для управления памятью
💻 Практический пример:
📎 Дополнительные ресурсы:
👉 Полное руководство по указателям в C
Поинтеры — это переменные, которые хранят адреса других переменных в памяти. Они активно используются в системном программировании, разработке встраиваемых систем и при работе с динамической памятью. Главная особенность — прямой доступ к памяти и эффективная работа с данными.
🔍 Ключевые концепции:
1️⃣ Объявление поинтера: int *ptr — создает указатель на целое число
2️⃣ Получение адреса: &variable — оператор для получения адреса переменной
3️⃣ Разыменование: *ptr — доступ к значению по адресу в указателе
4️⃣ Арифметика указателей: ptr++ перемещает указатель на следующий элемент
5️⃣ Связь с массивами: array[i] эквивалентно *(array + i)
6️⃣ Динамическая память: malloc() и free() для управления памятью
💻 Практический пример:
int array[] = {1, 2, 3, 4, 5};
int *ptr = array;
for(int i = 0; i < 5; i++) {
printf(«%d «, *ptr); ptr++;
}
// Вывод: 1 2 3 4 5
📎 Дополнительные ресурсы:
👉 Полное руководство по указателям в C
❤1👍1👏1
Книги по C++.
Скачивайте и читайте.
Beginning C++ Compilers
Автор: Ademi B. Ospanova
Modern C++ for Absolute Beginners
Автор: Slobodan Dmitrović
C++20: Get the Details
Автор: Grimm Rainer
Starting Out with C++ from Control Structures to Objects
Автор: Tony Gaddis
Software Architecture with C++
Автор: Adrian Ostrowski
Expert C++
Автор: Araks Tigranyan
Современный C++ безопасное использование
Автор: Джон Лакос
Методы программирования в задачах и примерах на C/C++
Автор: А. А. Корнев
Программирование на языке C++. Практический курс
Автор: Огнева М.
Самоучитель "Уроки по C++"
Автор: Ravesli
#cpp #подборка
Скачивайте и читайте.
Beginning C++ Compilers
Автор: Ademi B. Ospanova
Modern C++ for Absolute Beginners
Автор: Slobodan Dmitrović
C++20: Get the Details
Автор: Grimm Rainer
Starting Out with C++ from Control Structures to Objects
Автор: Tony Gaddis
Software Architecture with C++
Автор: Adrian Ostrowski
Expert C++
Автор: Araks Tigranyan
Современный C++ безопасное использование
Автор: Джон Лакос
Методы программирования в задачах и примерах на C/C++
Автор: А. А. Корнев
Программирование на языке C++. Практический курс
Автор: Огнева М.
Самоучитель "Уроки по C++"
Автор: Ravesli
#cpp #подборка
Программирование на
языке C++. Практический курс
Авторы: Огнева М., Кудрина Е.
Год издания: 2022
#cpp #ru
Скачать книгу
языке C++. Практический курс
Авторы: Огнева М., Кудрина Е.
Год издания: 2022
#cpp #ru
Скачать книгу
Telegram
Что по книжкам?
Modern C++ for Absolute Beginners
Автор: Slobodan Dmitrović
Год издания: 2023
#cpp #en #2O23
Скачать книгу
Автор: Slobodan Dmitrović
Год издания: 2023
#cpp #en #2O23
Скачать книгу