Ответ
AutoResetEvent — это примитив синхронизации потоков из пространства имен System.Threading. Он действует как "турникет" или сигнальный механизм "один к одному": один вызов Set() разблокирует ровно один ожидающий поток, после чего событие автоматически сбрасывается в несигнальное состояние.
Основные методы:
WaitOne(): Блокирует текущий поток до тех пор, пока событие не получит сигнал (вызовSet()).Set(): Переводит событие в сигнальное состояние, разрешая продолжить выполнение одному ожидающему потоку. Если ожидающих потоков нет, состояние остается сигнальным до первого вызоваWaitOne().
Практический пример (производитель-потребитель):
// Событие, сигнализирующее о наличии данных
private AutoResetEvent _dataReady = new AutoResetEvent(false);
private Queue<string> _buffer = new Queue<string>();
void Producer()
{
while (true)
{
var data = GenerateData();
lock (_buffer) { _buffer.Enqueue(data); }
// Сигнализируем потребителю, что данные готовы
_dataReady.Set();
Thread.Sleep(1000);
}
}
void Consumer()
{
while (true)
{
// Ожидаем сигнала от производителя
_dataReady.WaitOne();
string data;
lock (_buffer) { data = _buffer.Dequeue(); }
ProcessData(data);
}
}
Ключевое отличие от ManualResetEvent:
AutoResetEvent:Set()разблокирует один поток и автоматически сбрасывается. Как турникет, который пропускает одного человека и закрывается.ManualResetEvent:Set()переводит событие в сигнальное состояние и оставляет его открытым, разблокируя все ожидающие потоки. Чтобы закрыть, требуется явный вызовReset(). Работает как ворота.
Современная альтернатива: Для новых проектов часто предпочтительнее использовать SemaphoreSlim или TaskCompletionSource<T>, которые лучше интегрируются с асинхронным кодом (async/await).
Ответ 18+ 🔞
Давай разберём эту штуку, AutoResetEvent, чтобы было понятно, как она работает, а не просто набор страшных слов из документации.
Представь себе турникет в метро, блядь. Один прокрутил — один прошёл, и он тут же, сука, захлопывается. Пока следующий не прокрутит — никого не пустит. Вот это и есть AutoResetEvent в мире потоков. Примитив синхронизации, который говорит: «Стой тут, мудила, пока я тебе не разрешу пройти, и пропущу ровно одного».
Основные кнопки на этом турникете:
WaitOne(): Это когда ты подходишь к закрытому турникету и стоишь, как идиот, ждёшь. Поток на этом месте просто встаёт колом, пока кто-то не нажмёт...Set(): А это как раз та самая кнопка «прокрутить». Нажал — турникет на одно движение открылся, один счастливчик прошёл, и тут же — хрясь! — снова закрылся. Автоматом, нахуй. За что и зовётся AutoReset.
Пример из жизни, чтобы въехать (производитель-потребитель):
Допустим, есть у нас один чувак, который печёт пирожки (Producer), и другой, который их жрёт (Consumer). Проблема в том, что жрать нечего, пока пирожок не испёкся.
// Это наш турникет. false — изначально закрыт, нихуя не проходи.
private AutoResetEvent _pieReady = new AutoResetEvent(false);
private Queue<string> _hotPies = new Queue<string>(); // Сюда пирожки складываем
void Baker() // Производитель, пекарь
{
while (true)
{
var pie = "Пирожок с мясом №" + DateTime.Now.Ticks;
lock (_hotPies) { _hotPies.Enqueue(pie); } // Испечённый пирожок — в лоток
// АГА! Пирожок готов! Кричим жруну: «Иди жри, один!»
_pieReady.Set();
Thread.Sleep(1000); // Отдыхаем секунду перед следующим
}
}
void Eater() // Потребитель, обжора
{
while (true)
{
// Тут наш жрун подходит к турникету. Пирожка нет? Ждём. Сидим, слюни пускаем.
_pieReady.WaitOne();
string yummyPie;
lock (_hotPies) { yummyPie = _hotPies.Dequeue(); } // Хватаем пирожок из лотка
Console.WriteLine("Съел: " + yummyPie + " Вкусно, бля!");
}
}
Работает так: пекарь испёк — крикнул Set(). Жрун, который тупил на WaitOne(), просыпается, хватает пирожок и снова идёт ждать. Если пекарь быстрый и испёк два пирожка подряд, а жрун ещё первый не схватил, то второй вызов Set() просто так сгорит, потому что турникет уже открыт (никто не ждёт). Но это нормально, жрун придёт на следующий WaitOne() и пройдёт сразу, без ожидания — ему достанется второй пирожок.
А в чём, сука, разница с ManualResetEvent?
Вот это важно, а то все путаются.
AutoResetEvent— это тот самый турникет.Set()— один прошёл, и сразу автоматически закрылось.Reset()вызывать не надо.ManualResetEvent— это распашные ворота.Set()— ворота открылись настежь, и все, кто ждал, и кто придёт потом, — все проходят. Чтобы закрыть, надо вручную (Manual) подойти и вызватьReset(). Пока не закроешь — свободный проход.
Что использовать сегодня?
Честно? Если пишешь новый код и он асинхронный (эти твои async/await), то AutoResetEvent — это как приехать на тракторе на Формулу-1. Работает, но все будут ржать. Лучше смотреть в сторону SemaphoreSlim с его WaitAsync() или TaskCompletionSource<T>. Они современнее и с асинхронностью дружат. Но если копаешься в старом, многопоточном коде, или делаешь что-то простое и низкоуровневое — AutoResetEvent ещё вполне себе живой дед, который своё дело знает.