Событийно-ориентированное программирование (СОП) – это парадигма, в которой вычисления организованы вокруг событий (внешних или внутренних), очереди событий, диспетчеризации и обработчиков. В отличие от линейного (последовательного) стиля, здесь фундаментом служат реакции на асинхронные стимулы: нажатия клавиш, сетевые пакеты, таймеры, изменения состояний датчиков, сигналы ОС. Для абитуриента ЕГЭ эта тема полезна как практическая «обвязка» ключевых разделов: алгоритмы и структуры данных (очередь, стек, приоритеты), логика и конечные автоматы (таблицы переходов), кодирование/декодирование (формат события), сложность (оценка времени реакции и пропускной способности).
Формальная модель событийной системы
Рассмотрим систему как кортеж:
Σ = (S, E, Q, H, δ, s0)
где
S – множество состояний;
E – множество типов событий; элемент события – кортеж e = (type, payload, t) с моментом времени t;
Q – очередь событий (обычно FIFO, иногда – приоритетная);
H – набор обработчиков h: (s, e) → (s', out);
δ – функция диспетчеризации, выбирающая обработчик из H для e;
s0 – начальное состояние.
Исполнение: события поступают в Q; диспетчер δ извлекает верхнее событие e, вызывает соответствующий обработчик h, который возвращает новое состояние s' и (опционально) порождает выходное событие/действие out. Цикл повторяется, пока Q не пуст.
Конечные автоматы
СОП естественно описывается детерминированными конечными автоматами (ДКА). Формально:
M = (S, A, δ, s0, F)
где A – алфавит входов (типы событий), F – подмножество принимающих состояний. Переход:
δ: S × A → S
На практике A – сигнатуры событий, δ – таблица переходов/обработчиков, S – режимы интерфейса (ожидание, ввод, подтверждение и т.д.).
Модель времени и причинности
Для корректности реакций важны:
Метка времени t(e) и порядок обработки (FIFO по t, либо приоритет с частичным порядком).
Дедлайны: событие считается просроченным, если now - t(e) > D_max.
Дебаунс/троттлинг: регуляризация бурстов (см. §4.5).
Событийный цикл (event loop)
Базовый алгоритм:
state := s0
Q := ∅
while true do
e := Q.pop() // блокирующее ожидание или проверка
h := δ(e.type) // диспетчеризация по типу
(state, out) := h(state, e)
emit(out) // генерация побочных эффектов/вторичных событий
end
Важные варианты:
Один поток исполнения (GUI, JS в браузере) – неблокирующие обработчики.
Пулы потоков (серверы) – параллельная обработка; требуется синхронизация состояния.
RT-loop (встроенные системы) – строгие тайминги, предсказуемая латентность.
Очереди и приоритеты
Типы Q:
FIFO для обычных событий;
приоритетная (min-heap/max-heap) – для таймеров и служебных сигналов;
кольцевая (ring buffer) – фиксированная емкость в системах реального времени.
Оценки сложности:
push, pop в FIFO: O(1) амортизированно;
в куче: O(log n);
в кольцевом буфере при отсутствии переполнения: O(1).
Варианты передачи управления и данных
Callbacks: функция обратного вызова f(e);
Promise/Future: контейнер будущего результата;
async/await: синтаксический сахар над корутинами;
Pub/Sub: декуплинг источников и подписчиков через шину событий
JavaScript (однопоточный event loop)
document.addEventListener('click', (ev) => {
// обработчик короткий и неблокирующий
queueMicrotask(() => console.log('clicked at', ev.clientX, ev.clientY));
});
Правило: никаких долгих вычислений в обработчиках UI; выносите тяжёлые задачи в Web Workers/сервер.
Python (asyncio)
import asyncio
async def handler(e):
# неблокирующая реакция
await asyncio.sleep(0) # уступить управление
return f"handled {e}"
async def main():
events = ["A","B","C"]
tasks = [asyncio.create_task(handler(e)) for e in events]
for t in tasks:
print(await t)
asyncio.run(main())
Правило: I/O – через await; CPU-связанные вычисления – в ProcessPoolExecutor/ThreadPoolExecutor.
C# (.NET events, async/await)
public event EventHandler<DataEventArgs>? DataReady;
private async Task OnTickAsync()
{
var data = await FetchAsync(); // неблокирующая операция
DataReady?.Invoke(this, new DataEventArgs(data));
}
Правило: подписчики обязаны быть короткими и устойчивыми к исключениям; при необходимости – отмена CancellationToken.

Чистота и детерминизм
Обработчик h(s, e) должен по возможности быть чистым: не обращаться к внешним изменяемым данным, возвращать новое состояние s'.
Детерминированность достигается фиксированным порядком диспетчеризации и явной сериализацией критических секций.
Ограничение времени обработки
Пусть T_h – время обработки события, λ – интенсивность поступления (событий/с). Условие устойчивости очереди:
ρ = λ · T_h < 1
Если ρ ≥ 1, очередь будет расти – требуется снижение T_h (оптимизация), ограничение λ (пропуск/сэмплинг), параллелизм.
Идемпотентность и повторная доставка
Обработчик должен быть идемпотентным: повторная обработка того же e не меняет результат. Это устраняет классы ошибок при повторной доставке и восстановлении.
Контракты событий
Событие документируют как тип:
type: string
payload: структура (поля, типы, единицы измерения)
t: время (UTC, монотонные тики)
id: уникальный идентификатор (для дедупликации)
Нарушение контракта – источник расхождений состояний.
Дебаунс и троттлинг
Дебаунс: исполняем обработчик, если событие сохранялось непрерывно K шагов/Δt времени.
Троттлинг: ограничиваем частоту вызовов не чаще 1/Δt.
Эти техники стабилизируют бурсты и снижают λ.
Исключения, отмена, тайм-ауты
Любое ожидание снабжается тайм-аутом T_out.
Отмена – через токены/флаги; обработчики обязаны корректно завершаться.
Исключения не просачиваются за пределы обработчика: логирование, перевод в «безопасное» состояние.
Видимость и синхронизация
В многопоточной диспетчеризации:
доступ к общему состоянию – через мьютексы/очереди сообщений;
для микрозадач – lock-free структуры (CAS), но только при строгом тест-плане.
Устойчивость очереди:
ρ = λ · T_h // должно быть < 1
Дебаунс по K отсчётам (скользящее окно из K битов):
R := ((R << 1) | b) & ((1 << K) - 1)
if R == (1 << K) - 1 then S := 1
if R == 0 then S := 0
Дедлайн события:
isLate(e) := (now - t(e)) > D_max
Таблица переходов ДКА:
s' = δ[s, a] // двумерная таблица по состоянию и входу
Приоритетная диспетчеризация: событие с меньшим deadline имеет больший приоритет.
Упражнение 1. «Кольцевая очередь и устойчивость»
Условие. В систему приходят события со средней интенсивностью λ = 200 событий/с. Среднее время обработки одного события T_h = 3 мс.
Решение.
N_in = 2λ · Δt = 400 · 0.1 = 40
За то же время система успеет обработать:
N_out = Δt / T_h = 0.1 / 0.003 ≈ 33.33 → 33 события
Максимальный прирост в очереди:
ΔQ = N_in − N_out ≈ 40 − 33 = 7
Ответ: ёмкость ≥ 7 дополнительных слотов сверх текущей загрузки. С запасом выбирают C ≥ 8–10.
Упражнение 2. «Дебаунс по K отсчётам: логика и таблица истинности»
Условие. Кнопка даёт шумный битовый поток b(t). Стабильное состояние S меняется только если подряд пришли K=3 одинаковых значений (1 → «нажато», 0 → «отпущено»).
Решение.
R := ((R << 1) | b) & 0b111
if S = 0 and R = 0b111 then S := 1
if S = 1 and R = 0b000 then S := 0
Смена 0→1 при R = 111, смена 1→0 при R = 000; в иных случаях S сохраняется. Это иллюстрация конечного автомата и таблиц истинности (темы ЕГЭ по логике).
Упражнение 3. «ДКА для ввода PIN: три звезды и решётка»
Условие. Ввод с клавиатуры приходит как события из множества A = {*, #, d}, где d – цифра. Строка считается принятой, если пользователь ввёл ровно три цифры, затем #. Любое другое событие сбрасывает ввод в начальное состояние. Построить таблицу переходов.
Решение.
Состояния: S0 – начало, S1 – введена 1 цифра, S2 – 2 цифры, S3 – 3 цифры, F – принято.
Переходы:
S0 --d--> S1; S0 --*,#--> S0
S1 --d--> S2; S1 --*,#--> S0
S2 --d--> S3; S2 --*,#--> S0
S3 --#--> F ; S3 --d, *--> S0
F --*,#,d--> S0 // после приёма – сброс
Это типовая задача ЕГЭ на построение конечных автоматов и анализ регулярных шаблонов.
Упражнение 4. «Планировщик: успеем ли к дедлайну?»
Условие. События типа A и B приходят в моменты времени (мс):
A: 0, 10, 40, 70
B: 5, 15, 45
Время обработки T_h = 12 мс; обработчик один, очередь FIFO. Сколько событий будет обработано за интервал [0; 60] мс включительно (момент завершения обработки ≤ 60)?
Решение.
Сформируем общий поток по времени прибытия: 0(A), 5(B), 10(A), 15(B), 40(A), 45(B), 70(A)
Эмуляция:
До t=60 завершены обработки в моменты 12, 24, 36, 52 – 4 события.
Упражнение 5. «Парсинг журнала событий: агрегирование по типам»
Условие. Дана строка журнала L, содержащая записи вида type@timestamp; (без пробелов), например:
"A@10;B@12;A@15;C@20;B@23;A@30;".
Напишите функцию countByType(L), возвращающую словарь частот типов.
Решение (Python):
def countByType(L: str) -> dict[str, int]:
freq = {}
for token in filter(None, L.split(';')):
t, _ = token.split('@')
freq[t] = freq.get(t, 0) + 1
return freq
# Пример:
# countByType("A@10;B@12;A@15;C@20;B@23;A@30;") -> {'A': 3, 'B': 2, 'C': 1}
Это задача ЕГЭ на строки, словари, подсчёт частот.
Идемпотентный обработчик (Python, псевдо-бизнес-ключ)
def handle_payment(state, event):
if event.id in state['done']: # защита от повтора
return state, None
# ... проверить контракт payload ...
new_state = dict(state)
new_state['balance'] += event.payload['amount']
new_state['done'].add(event.id)
return new_state, {'type':'LOG', 'payload':{'msg':'ok', 'id':event.id}}
Троттлинг (JS)
function throttle(fn, wait) {
let last = 0;
return function(...args) {
const now = Date.now();
if (now - last >= wait) {
last = now;
return fn.apply(this, args);
}
};
}
Диспетчеризация по типу (таблица переходов)
handlers = {
'CLICK': on_click,
'TICK': on_tick,
'DATA': on_data,
}
def dispatch(state, event):
h = handlers.get(event.type, on_unknown)
return h(state, event)
Событийно-ориентированное программирование – это строго формализуемая практика построения программ как реакций на дискретные стимулы. Формальная модель (S, E, Q, H, δ) объединяет конечные автоматы, очереди и диспетчеризацию, а инженерные правила – чистоту обработчиков, устойчивость по нагрузке, идемпотентность и управляемые побочные эффекты – обеспечивают предсказуемость и надёжность. Для ЕГЭ эта тема служит «мостом» между логикой, автоматами, строками и оценками сложности. Освоив предложенные упражнения, шпаргалку и чек-лист, вы научитесь переводить задачи из экзаменационного формата в работающие событийные решения – и обратно, что резко повышает качество и скорость ваших ответов на экзамене.