ИУП: интенсив 7 дней + диагностический пакет (32 вопроса, 9 задач с автопроверкой)

This commit is contained in:
Kodlo-chan
2026-09-23 16:05:09 +07:00
commit 4cd63b3d4d
40 changed files with 2464 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
// Задача 06 — потокобезопасная очередь. Заполни реализацию.
#include <cstddef>
#include <stdexcept>
class BlockingQueue {
public:
explicit BlockingQueue(size_t capacity) : cap_(capacity) {}
~BlockingQueue() = default;
void push(int v) {
(void)v;
// TODO: должен блокироваться на полной очереди и бросать std::runtime_error после close()
throw std::runtime_error("push: not implemented");
}
bool pop(int& out) {
(void)out;
return false; // TODO
}
void close() {
// TODO
}
size_t size() const { return 0; }
private:
size_t cap_;
};
+27
View File
@@ -0,0 +1,27 @@
# Задача 06 — потокобезопасная ограниченная очередь (C++)
Классический вопрос на собеседовании в embedded/сетевую разработку: не «знаешь ли ты
std::thread», а «умеешь ли ты не сломать счётчик под нагрузкой».
```c++
class BlockingQueue {
public:
explicit BlockingQueue(size_t capacity);
~BlockingQueue();
void push(int v); // блокируется, пока очередь полна
bool pop(int& out); // блокируется, пока пуста; false — если закрыта и пуста
void close(); // после close: pop() опустошает остаток и отдаёт false
size_t size() const;
};
```
Требования:
- push после close() бросает `std::runtime_error`;
- закрытие разблокирует все ждущие потоки (никакого вечного ожидания и busy-wait);
- размер очереди никогда не превышает capacity;
- ни одной гонки: тест собирается и гоняется с ThreadSanitizer (в том числе `size()`
вызывается из другого потока — он тоже должен быть потокобезопасным).
Проверка: `python3 grade.py 06`. Критерий: все `ok`, TSan чистый, нет зависания.
Разбор после сдачи: почему нужен predicate-цикл вокруг wait, и как один condition_variable
на оба события даёт ложные пробуждения.
+73
View File
@@ -0,0 +1,73 @@
#include <cstdio>
#include <thread>
#include <vector>
#include <atomic>
#include <mutex>
#include <stdexcept>
#include "solution.cpp"
static int failures = 0;
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
int main() {
// Базовая дисциплина
{
BlockingQueue q(2);
q.push(1);
q.push(2);
CHECK(q.size() == 2, "size() == 2 после двух push");
int v = 0;
CHECK(q.pop(v) && v == 1, "pop -> 1 (FIFO)");
CHECK(q.pop(v) && v == 2, "pop -> 2 (FIFO)");
q.close();
CHECK(!q.pop(v), "после close и опустошения pop -> false");
bool threw = false;
try { q.push(3); } catch (const std::runtime_error&) { threw = true; }
CHECK(threw, "push после close -> std::runtime_error");
}
// Продюсеры/потребители под нагрузкой
{
const int P = 4, C = 4, PER = 20000, C3 = 64;
BlockingQueue q(C3);
std::atomic<int> pushed{0};
std::atomic<long long> sum{0};
std::atomic<int> popped{0};
std::atomic<int> over{0};
auto producer = [&](int pid) {
for (int i = 0; i < PER; ++i) {
q.push(pid * 1000000 + i);
pushed.fetch_add(1, std::memory_order_relaxed);
if (q.size() > C3) over.fetch_add(1, std::memory_order_relaxed);
}
};
auto consumer = [&](int) {
int v = 0;
while (q.pop(v)) {
sum.fetch_add(v, std::memory_order_relaxed);
popped.fetch_add(1, std::memory_order_relaxed);
}
};
std::vector<std::thread> ts;
for (int i = 0; i < C; ++i) ts.emplace_back(consumer, i);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
for (int i = 0; i < P; ++i) ts.emplace_back(producer, i);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
while (pushed.load() < P * PER) std::this_thread::sleep_for(std::chrono::milliseconds(10));
q.close();
for (auto& t : ts) t.join();
long long expect = 0;
for (int p = 0; p < P; ++p) for (int i = 0; i < PER; ++i) expect += (long long)p * 1000000 + i;
std::printf(" (вставлено=%d, извлечено=%d, переполнений=%d)\n",
pushed.load(), popped.load(), over.load());
CHECK(pushed.load() == P * PER, "все вставки выполнены (нет потерь)");
CHECK(popped.load() == P * PER, "извлечено ровно столько же (нет дублей/потерь)");
CHECK(sum.load() == expect, "сумма значений совпадает с эталоном");
CHECK(over.load() == 0, "size() не превышал capacity");
}
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
return failures ? 1 : 0;
}