#вопросы_с_собеседований
Что такое барьеры памяти?
В C++ барьеры памяти являются механизмами синхронизации, используемыми для контроля порядка и видимости операций с памятью в многопоточных программах. Они гарантируют, что операции с памятью выполняются в определенном порядке и что эффекты одной операции видны другим потокам предсказуемым образом.
Существует несколько типов барьеров памяти, которые можно использовать в C++:
- Барьер приобретения: Гарантирует, что последующие операции с памятью не могут быть переупорядочены до барьера.
- Барьер освобождения: Гарантирует, что предшествующие операции с памятью не могут быть переупорядочены после барьера.
- Полный барьер памяти: Обеспечивает семантику как приобретения, так и освобождения.
- Барьер чтения-записи: Гарантирует, что предшествующие операции чтения не могут быть переупорядочены последующими операциями записи.
В C++ барьеры памяти обычно реализуются с помощью атомарных операций или примитивов синхронизации, предоставляемых языком, таких как std::atomic_thread_fence, std::atomic_signal_fence, или мьютексов и переменных состояния.
Что такое барьеры памяти?
Существует несколько типов барьеров памяти, которые можно использовать в C++:
- Барьер приобретения: Гарантирует, что последующие операции с памятью не могут быть переупорядочены до барьера.
- Барьер освобождения: Гарантирует, что предшествующие операции с памятью не могут быть переупорядочены после барьера.
- Полный барьер памяти: Обеспечивает семантику как приобретения, так и освобождения.
- Барьер чтения-записи: Гарантирует, что предшествующие операции чтения не могут быть переупорядочены последующими операциями записи.
В C++ барьеры памяти обычно реализуются с помощью атомарных операций или примитивов синхронизации, предоставляемых языком, таких как std::atomic_thread_fence, std::atomic_signal_fence, или мьютексов и переменных состояния.
#вопросы_с_собеседований
Что такое CI/CD и какие преимущества дает разработчику?
CI/CD означает непрерывную интеграцию и непрерывное развертывание. Это набор практик и инструментов, используемых в разработке программного обеспечения для автоматизации процесса создания, тестирования и развертывания приложений.
Непрерывная интеграция включает в себя интеграцию изменений кода от нескольких разработчиков в общий репозиторий часто, как правило, несколько раз в день. Каждая интеграция запускает автоматизированный процесс сборки и тестирования для раннего обнаружения и решения проблем интеграции.
Непрерывное развертывание (или Continuous Delivery) направлено на автоматизацию процесса развертывания программного обеспечения в производственных средах. Он включает в себя автоматизацию шагов, необходимых для упаковки, развертывания и настройки приложения.
Вот некоторые преимущества CI/CD для разработчиков C++:
1. Раннее обнаружение ошибок.
2. Более быстрый цикл обратной связи.
3. Качество и ремонтопригодность кода.
4. Сотрудничество и коммуникация между разработчиками.
5. Автоматизация сборки, тестирования и развертывания кода.
6. Непрерывное развертывание.
7. Масштабируемость.
Что такое CI/CD и какие преимущества дает разработчику?
Непрерывная интеграция включает в себя интеграцию изменений кода от нескольких разработчиков в общий репозиторий часто, как правило, несколько раз в день. Каждая интеграция запускает автоматизированный процесс сборки и тестирования для раннего обнаружения и решения проблем интеграции.
Непрерывное развертывание (или Continuous Delivery) направлено на автоматизацию процесса развертывания программного обеспечения в производственных средах. Он включает в себя автоматизацию шагов, необходимых для упаковки, развертывания и настройки приложения.
Вот некоторые преимущества CI/CD для разработчиков C++:
1. Раннее обнаружение ошибок.
2. Более быстрый цикл обратной связи.
3. Качество и ремонтопригодность кода.
4. Сотрудничество и коммуникация между разработчиками.
5. Автоматизация сборки, тестирования и развертывания кода.
6. Непрерывное развертывание.
7. Масштабируемость.
Движок C3D Vision управляет отображением 3D-текстур «на лету»
Texture3D представляет стандартный C++ класс и служит для обработки 3D-текстур.
Смотреть статью
Texture3D представляет стандартный C++ класс и служит для обработки 3D-текстур.
Смотреть статью
Хабр
Движок C3D Vision управляет отображением 3D-текстур «на лету»
Движок визуализации C3D Vision пополнился новым функционалом. Теперь у пользователей есть возможность создания объемных текстур и отображения их в сцене. В этой заметке мы расскажем об объектах API...
std::conditional_variable
std::condition_variable является частью стандартной библиотеки и позволяет потоку ожидать наступления определенного условия перед продолжением выполнения.
Основной метод
В этом примере создается поток
std::condition_variable является частью стандартной библиотеки и позволяет потоку ожидать наступления определенного условия перед продолжением выполнения.
Основной метод
std::condition_variable - это wait(), который блокирует вызывающий поток до тех пор, пока другой поток не оповестит его с помощью метода notify_one() или notify_all(). Когда вызывается wait(), текущий поток освобождает блокировку, которая должна быть захвачена вместе с вызовом wait(), и ожидает, пока другой поток не вызовет один из методов оповещения. После получения оповещения поток пробуждается и пытается повторно захватить блокировку, и если блокировка успешно захватывается, поток может продолжить свою работу.В этом примере создается поток
workerThread, который ожидает сигнала от основного потока. Основной поток засыпает на 2 секунды, а затем устанавливает флаг ready в true и оповещает ожидающий поток с помощью notify_one(). workerThread пробуждается и выводит сообщение в консоль.std::atomic_flag
Метод
Метод
В этом примере создаются два потока
std::atomic_flag является классом для реализации простой атомарной флаговой переменной. Он предоставляет механизм безопасной работы с флагом в многопоточной среде без необходимости использования блокировок.std::atomic_flag имеет два основных метода: test_and_set() и clear():Метод
test_and_set() устанавливает флаг в "истину" и возвращает предыдущее значение флага. Если флаг уже был установлен, вызов test_and_set() вернет true, в противном случае он вернет false.Метод
clear() сбрасывает флаг в "ложь".В этом примере создаются два потока
workerThread1 и workerThread2, которые пытаются получить доступ к защищенному блоку кода. Флаг flag инициализируется значением true с помощью ATOMIC_FLAG_INIT. В цикле worker() каждый поток пытается вызвать test_and_set() для захвата флага. Если флаг уже установлен, поток ждет и повторно пытается его захватить. Когда флаг наконец захватывается, поток выполняет некоторую работу и вызывает clear() для освобождения флага.Базовый курс лекций по С++ из бакалавриата МФТИ
https://youtu.be/Bym7UMqpVEY?list=PL3BR09unfgciJ1_K_E914nohpiOiHnpsK
https://youtu.be/Bym7UMqpVEY?list=PL3BR09unfgciJ1_K_E914nohpiOiHnpsK
YouTube
Базовый курс C++ (MIPT, ILab). Lecture 1. Scent of C++.
Лекции в бакалавриате МФТИ по C++ на русском языке.
Это первая лекция базового курса. На этой лекции мы познакомимся с двумя базовыми механизмами языка C++, разберем его отличия от C.
Лектор: Константин Владимиров
Дата лекции: 13 сентября 2021 года
Съёмка:…
Это первая лекция базового курса. На этой лекции мы познакомимся с двумя базовыми механизмами языка C++, разберем его отличия от C.
Лектор: Константин Владимиров
Дата лекции: 13 сентября 2021 года
Съёмка:…
thread-safe пул потоков
На первом изображении представлена реализация потокобезопасного пула потоков. В этой реализации класс
На втором изображении представлен пример использования. функция
Обратите внимание, что приведенный здесь код является базовой реализацией пула потоков и не включает в себя расширенные возможности, такие как обработка возвращаемых значений от задач или ограничение количества одновременных задач. Вы можете расширить эту реализацию в соответствии с вашими конкретными потребностями.
На первом изображении представлена реализация потокобезопасного пула потоков. В этой реализации класс
ThreadPool управляет коллекцией рабочих потоков. Количество рабочих потоков определяется при создании пула потоков. Функция enqueue используется для передачи заданий в пул потоков.На втором изображении представлен пример использования. функция
Task передается в пул потоков с помощью функции enqueue. Функция Task просто печатает сообщение и "спит" в течение 1 секунды. После отправки всех заданий главный поток ждет некоторое время, чтобы дать заданиям завершиться перед выходом.Обратите внимание, что приведенный здесь код является базовой реализацией пула потоков и не включает в себя расширенные возможности, такие как обработка возвращаемых значений от задач или ограничение количества одновременных задач. Вы можете расширить эту реализацию в соответствии с вашими конкретными потребностями.
std::async
В C++
Функция
В этом примере функция
Функция
В C++
std::async - это шаблон функции, предоставляемый стандартной библиотекой <future>. Он используется для асинхронного выполнения функции или вызываемого объекта и получения объекта future, представляющего результат вычислений.Функция
std::async создает новый поток или использует существующий поток из пула потоков реализации для выполнения поставленной задачи. Она возвращает объект std::future, который можно использовать для получения результата или статуса вычисления.В этом примере функция
AddNumbers выполняется асинхронно с использованием std::async. Она принимает два целых числа в качестве аргументов и возвращает их сумму. Функция std::async вызывается с помощью AddNumbers и аргументов 5 и 10.Функция
std::async возвращает объект std::future<int>, который представляет собой результат вычисления. Вызывая функцию get() на объекте future, мы блокируем выполнение до завершения вычислений и получаем результат.std::launch::async
При использовании
В этом примере мы создаем асинхронную операцию с помощью
Затем мы вызываем
std::launch::async - это один из флагов, которые можно использовать при запуске асинхронной операции с помощью std::async. Он указывает, что операция должна быть запущена немедленно в отдельном потоке.При использовании
std::launch::async с std::async создается новый поток выполнения для асинхронной операции. Это означает, что операция будет выполняться параллельно с основным потоком, который вызвал std::async. Фактическое время запуска операции зависит от реализации, но обычно она начинается сразу после вызова std::async.В этом примере мы создаем асинхронную операцию с помощью
std::async и флага std::launch::async. Операция calculateSum складывает два числа, но перед этим засыпает на 2 секунды для имитации длительной работы.Затем мы вызываем
futureSum.get(), чтобы получить результат асинхронной операции. Если результат еще не готов, основной поток будет блокирован до его завершения.#вопросы_с_собеседований
Как размещается в памяти класс с множественным наследованием и виртуальными функциями в с++?
- Каждый класс в иерархии наследования имеет свой собственный блок памяти, называемый подобъектом базового класса.
- Порядок размещения подобъектов определяется порядком, в котором они перечислены при определении класса-наследника.
- Для каждого класса-потомка, содержащего виртуальные функции, компилятор создает таблицу виртуальных функций, она содержит указатели на виртуальные функции класса.
- У каждого объекта класса, содержащего виртуальные функции, есть указатель на его таблицу виртуальных функций (VTable pointer).
- Размер класса равен сумме размеров его подобъектов базовых классов плюс любые дополнительные данные, определенные в самом классе.
- При вызове виртуальной функции компилятор использует указатель на VTable объекта, чтобы найти правильную функцию для вызова.
*Реализация размещения таких классов может отличаться в зависимости от компилятора и платформы, на которой выполняется код. Описанный выше процесс является общим принципом, но некоторые детали могут различаться.
Как размещается в памяти класс с множественным наследованием и виртуальными функциями в с++?
- Порядок размещения подобъектов определяется порядком, в котором они перечислены при определении класса-наследника.
- Для каждого класса-потомка, содержащего виртуальные функции, компилятор создает таблицу виртуальных функций, она содержит указатели на виртуальные функции класса.
- У каждого объекта класса, содержащего виртуальные функции, есть указатель на его таблицу виртуальных функций (VTable pointer).
- Размер класса равен сумме размеров его подобъектов базовых классов плюс любые дополнительные данные, определенные в самом классе.
- При вызове виртуальной функции компилятор использует указатель на VTable объекта, чтобы найти правильную функцию для вызова.
*Реализация размещения таких классов может отличаться в зависимости от компилятора и платформы, на которой выполняется код. Описанный выше процесс является общим принципом, но некоторые детали могут различаться.
std::hash
Это структура шаблонного класса, определенная в заголовочном файле
*Для пользовательского типа данных требуется явная специализация структуры
Это структура шаблонного класса, определенная в заголовочном файле
<functional>. Она предоставляет хеш-функцию для хэширования различных типов данных, включая встроенные и пользовательские.std::hash используется, например, в ассоциативных контейнерах, таких как std::unordered_map и std::unordered_set, для быстрого доступа к элементам по ключу.*Для пользовательского типа данных требуется явная специализация структуры
std::hash для корректной работы хэширования.#вопросы_с_собеседований
Как работает spinlock?
Spinlock - это примитив синхронизации, который используется для организации доступа к общему ресурсу между несколькими потоками в многопоточной среде. Он работает по принципу активного ожидания (busy-waiting), когда поток, не получив доступ к ресурсу, повторно проверяет условие блокировки без перехода в режим ожидания или сна.
Основная идея spinlock состоит в том, чтобы поток, желающий получить доступ к ресурсу, повторял быструю проверку состояния блокировки в цикле, называемом "spin". Если блокировка уже удерживается другим потоком, текущий поток продолжает повторять проверку до тех пор, пока блокировка не будет освобождена.
Однако, важно отметить, что использование spinlock не всегда является эффективным. Если потоки занимаются активным ожиданием без перехода в режим ожидания, это может привести к высокому использованию процессорного времени. Spinlock особенно полезен, когда блокировки очень короткие и вероятность конфликтов невысока.
Как работает spinlock?
Основная идея spinlock состоит в том, чтобы поток, желающий получить доступ к ресурсу, повторял быструю проверку состояния блокировки в цикле, называемом "spin". Если блокировка уже удерживается другим потоком, текущий поток продолжает повторять проверку до тех пор, пока блокировка не будет освобождена.
Однако, важно отметить, что использование spinlock не всегда является эффективным. Если потоки занимаются активным ожиданием без перехода в режим ожидания, это может привести к высокому использованию процессорного времени. Spinlock особенно полезен, когда блокировки очень короткие и вероятность конфликтов невысока.
std::launch::deferred
Когда операция запускается с этим флагом, она не выполняется немедленно в отдельном потоке. Вместо этого операция откладывается до момента, когда вызывается
В этом примере мы создаем асинхронную операцию с помощью
Однако, когда мы вызываем
std::launch::deferred - это флаг, который может быть использован при запуске асинхронной операции с помощью std::async. Он указывает, что операция должна быть отложена и выполнена только при вызове функции get() на возвращаемом объекте std::future.Когда операция запускается с этим флагом, она не выполняется немедленно в отдельном потоке. Вместо этого операция откладывается до момента, когда вызывается
get() на объекте std::future. В этот момент операция будет выполнена синхронно в вызывающем потоке.В этом примере мы создаем асинхронную операцию с помощью
std::async и флага std::launch::deferred. Операция calculateSum складывает два числа и не имеет задержки.Однако, когда мы вызываем
std::future::get(), операция calculateSum будет выполнена синхронно в вызывающем потоке, а не в отдельном потоке. Это означает, что выполнение программы будет остановлено до завершения операции calculateSum.recursive mutex
Recursive mutex представляет собой механизм синхронизации, который позволяет одному потоку захватить мьютекс несколько раз без возникновения блокировки.
При каждом захвате поток должен также освободить мьютекс соответствующее количество раз, чтобы другие потоки могли получить доступ к нему.
При использовании
В этом примере потоки
Recursive mutex представляет собой механизм синхронизации, который позволяет одному потоку захватить мьютекс несколько раз без возникновения блокировки.
При каждом захвате поток должен также освободить мьютекс соответствующее количество раз, чтобы другие потоки могли получить доступ к нему.
При использовании
recursive mutex необходимо обеспечивать баланс между захватом и освобождением мьютекса, чтобы избежать утечек ресурсов и избыточной рекурсии.В этом примере потоки
t1 и t2 запускают recursiveFunction с разной глубиной и результат выполнения программы будет выводить глубину рекурсии, пока она не достигнет 0.Удаление всех вхождений элемента в массив
В этом методе сдвиньте нецелевой элемент в левую сторону.
• Проверьте, является ли текущий элемент целевым элементом или нет.
• Если это целевой элемент, увеличьте переменную cnt.
• После этого элемента все нецелевые элементы сдвинутся влево с промежутком (n-cnt).
Временная сложность: O(n)
Сложность пространства: O(1)
На картинке приведена программа на C ++ для удаления всех вхождений элемента из массива с использованием оптимизированного подхода.
В этом методе сдвиньте нецелевой элемент в левую сторону.
• Проверьте, является ли текущий элемент целевым элементом или нет.
• Если это целевой элемент, увеличьте переменную cnt.
• После этого элемента все нецелевые элементы сдвинутся влево с промежутком (n-cnt).
Временная сложность: O(n)
Сложность пространства: O(1)
На картинке приведена программа на C ++ для удаления всех вхождений элемента из массива с использованием оптимизированного подхода.
deque (double-ended queue)
deque — это контейнерный класс, в который можно эффективно добавлять и удалять элементы как с начала, так и с конца, а также получать произвольный доступ к элементам по индексу.
В этом примере мы используем функции
deque — это контейнерный класс, в который можно эффективно добавлять и удалять элементы как с начала, так и с конца, а также получать произвольный доступ к элементам по индексу.
deque фактически является двусторонней очередью.deque, как и vector, поддерживает произвольный доступ к элементам контейнера, но в отличие от вектора также поддерживает добавление в начало контейнера. Кроме того, во внутренней реализации deque при изменении размера не выделяет новый массив в памяти для вмещения нового набора элементов, а манипулирует указателями.В этом примере мы используем функции
push_front() и push_back() для добавления элементов в конец и начало очереди, а pop_front() и pop_back() для удаления первого и последнего элементов очереди numbers.#вопросы_с_собеседований
Что такое race condition?
Race condition — это ситуация, когда результат выполнения программы зависят от того, в каком порядке выполняются отдельные потоки или процессы.
При наличии race condition несколько потоков или процессов могут обращаться к общему ресурсу или переменной и пытаться изменить ее значение одновременно. Это может привести к непредсказуемым результатам и ошибкам в программе.
Чтобы этого избежать, стоит использовать мьютексы, семафоры или атомарные операции. Они позволят скоординировать доступ к общим ресурсам и обеспечить правильную последовательность выполнения операций в многопоточной среде.
Что такое race condition?
Чтобы этого избежать, стоит использовать мьютексы, семафоры или атомарные операции. Они позволят скоординировать доступ к общим ресурсам и обеспечить правильную последовательность выполнения операций в многопоточной среде.
#вопросы_с_собеседований
Что такое exception safety guarantee?
Exception safety guarantee — это гарантия, которую предоставляет код при возникновении исключительной ситуации (exception). Это означает, что код должен обрабатывать исключения таким образом, чтобы предотвратить утечку ресурсов и поддерживать объекты в согласованном состоянии.
Существует три уровня
Разработчики должны обеспечивать соответствующий уровень
Что такое exception safety guarantee?
Exception safety guarantee — это гарантия, которую предоставляет код при возникновении исключительной ситуации (exception). Это означает, что код должен обрабатывать исключения таким образом, чтобы предотвратить утечку ресурсов и поддерживать объекты в согласованном состоянии.
Существует три уровня
exception safety guarantee:- basic: Гарантирует, что нет утечек ресурсов, но в случае исключения никаких дополнительных гарантий не предоставляется, и объекты могут быть изменены или оставлены в неполноценном состоянии.- strong: Гарантирует, что нет утечек ресурсов и объекты остаются в исходном состоянии, если исключение возникает. Если операция не может быть выполнена, объекты не изменяются.- no-throw: Гарантирует, что операция не вызывает исключений. Это самый высокий уровень гарантии безопасности исключений.Разработчики должны обеспечивать соответствующий уровень
exception safety для своих классов и функций, чтобы гарантировать правильное и безопасное поведение при обработке исключений.Принцип RAII
Принцип
В этом примере автоматическое закрытие файла в деструкторе обеспечивает гарантированное освобождение ресурса, даже если происходит исключение.
А в функции
Resource Acquisition Is Initialization является важным концептом, который связывает временный срок жизни объекта с его ресурсами. Суть принципа заключается в том, что ресурсы, такие как память, файлы, сетевые соединения и т.д., должны быть приобретены при инициализации объекта и автоматически освобождены при его уничтожении.Принцип
RAII позволяет сделать управление ресурсами более безопасным и эффективным, освобождая программиста от ручного управления ресурсами и предотвращая утечки ресурсов.В этом примере автоматическое закрытие файла в деструкторе обеспечивает гарантированное освобождение ресурса, даже если происходит исключение.
А в функции
main файл закрывается автоматически, при выходе из блока try.Функции вместимости в строках
1. capacity() - функция возвращает ёмкость памяти, выделенную для строки, которая может быть равной или больше, чем размер самой строки. Дополнительное пространство выделяется таким образом, чтобы при добавлении новых символов в строку операции могли выполняться эффективно.
2. resize() - функция изменяет размер строки, его можно увеличивать или уменьшать.
3. length() - функция возвращает длину строки.
4. shrink_to_fit() - функция уменьшает ёмкость памяти строки, делает ее равной минимально возможной. Эта операция полезна для экономии дополнительной памяти, когда мы уверены, что больше не нужно добавлять символы.
1. capacity() - функция возвращает ёмкость памяти, выделенную для строки, которая может быть равной или больше, чем размер самой строки. Дополнительное пространство выделяется таким образом, чтобы при добавлении новых символов в строку операции могли выполняться эффективно.
2. resize() - функция изменяет размер строки, его можно увеличивать или уменьшать.
3. length() - функция возвращает длину строки.
4. shrink_to_fit() - функция уменьшает ёмкость памяти строки, делает ее равной минимально возможной. Эта операция полезна для экономии дополнительной памяти, когда мы уверены, что больше не нужно добавлять символы.