AutoResetEvent Сброс сразу после установки

Рассмотрим следующую схему:

private AutoResetEvent signal = new AutoResetEvent(false);

private void Work()
{
    while (true)
    {
        Thread.Sleep(5000);
        signal.Set();
        //has a waiting thread definitely been signaled by now?
        signal.Reset();
    }
}

public void WaitForNextEvent()
{
    signal.WaitOne();
}

Цель этого шаблона - позволить внешним потребителям ожидать определенного события (например, прибытие сообщения). WaitForNextEvent не вызывается из класса.

Чтобы привести пример, который должен быть знаком, рассмотрим System.Diagnostics.Process, Это подвергает Exited событие, но оно также подвергает WaitForExit метод, который позволяет вызывающей стороне синхронно ожидать завершения процесса. это то, что я пытаюсь достичь здесь.

Причина мне нужна signal.Reset() это если поток вызывает WaitForNextEvent после signal.Set() уже был вызван (или, другими словами, если .Set был вызван, когда ни один поток не ожидал), он немедленно возвращается, так как событие уже было сигнализировано ранее.

Вопрос

  • Гарантируется ли, что поток вызывает WaitForNextEvent() будет сигнализироваться раньше signal.Reset() называется? Если нет, то каковы другие решения для реализации WaitFor метод?

4 ответа

Решение

Вместо того, чтобы использовать AutoResetEvent или же ManualResetEvent, использовать этот:

public sealed class Signaller
{
    public void PulseAll()
    {
        lock (_lock)
        {
            Monitor.PulseAll(_lock);
        }
    }

    public void Pulse()
    {
        lock (_lock)
        {
            Monitor.Pulse(_lock);
        }
    }

    public void Wait()
    {
        Wait(Timeout.Infinite);
    }

    public bool Wait(int timeoutMilliseconds)
    {
        lock (_lock)
        {
            return Monitor.Wait(_lock, timeoutMilliseconds);
        }
    }

    private readonly object _lock = new object();
}

Затем измените ваш код следующим образом:

private Signaller signal = new Signaller();

private void Work()
{
    while (true)
    {
        Thread.Sleep(5000);
        signal.Pulse(); // Or signal.PulseAll() to signal ALL waiting threads.
    }
}

public void WaitForNextEvent()
{
    signal.Wait();
}

Там нет гарантии. Это:

AutoResetEvent flag = new AutoResetEvent(false);

new Thread(() =>
{
    Thread.CurrentThread.Priority = ThreadPriority.Lowest;
    Console.WriteLine("Work Item Started");
    flag.WaitOne();
    Console.WriteLine("Work Item Executed");
}).Start();

// For fast systems, you can help by occupying processors.
for (int ix = 0; ix < 2; ++ix)
{
    new Thread(() => { while (true) ; }).Start();
}

Thread.Sleep(1000);
Console.WriteLine("Sleeped");

flag.Set();
// Decomment here to make it work
//Thread.Sleep(1000);

flag.Reset();
Console.WriteLine("Finished");
Console.ReadLine();

не будет печатать "Задание выполнено" в моей системе. Если я добавлю Thread.Sleep между Set и Reset это печатает это. Обратите внимание, что это сильно зависит от процессора, поэтому вам может потребоваться создать потоки тома, чтобы "заполнить" процессоры. Обратите внимание, что на моем ПК это воспроизводимо в 50% случаев:-)

Для вышедших:

readonly object mylock = new object();

тогда где-то:

lock (mylock)
{
    // Your code goes here
}

и WaitForExit:

void WaitForExit()
{
    lock (mylock) ;
    // exited
}

void bool IsExited()
{
    bool lockTacken = false;

    try
    {
        Monitor.TryEnter(mylock, ref lockTacken);
    }
    finally
    {
        if (lockTacken)
        {
            Monitor.Exit(mylock);
        }
    }

    return lockTacken;
}

Обратите внимание, что lock конструкция не совместима с async/await (как и не все блокирующие примитивы.NET)

Я хотел бы использовать TaskCompletionSource s:

private volatile TaskCompletionSource<int> signal = new TaskCompletionSource<int>();

private void Work()
{
    while (true)
    {
        Thread.Sleep(5000);
        var oldSignal = signal;
        signal = new TaskCompletionSource<int>()
        //has a waiting thread definitely been signaled by now?
        oldSignal.SetResult(0);
    }
}

public void WaitForNextEvent()
{
    signal.Task.Wait();
}

К тому времени, когда код вызывает SetResult нет ввода нового кода WaitForNextEvent может получить TaskCompletionSource это сигнализируется.

Я считаю, что это не гарантировано.

Однако твой логический поток не понят мной. Если ваша основная тема Sets сигнал, почему он должен ждать, пока этот сигнал достигнет пункта назначения? Не лучше ли продолжить вашу логику "после набора сигналов" в том потоке, который ожидал?

Если вы не можете сделать это, я рекомендую вам использовать второй WaitHandle сигнализировать первому потоку, что второй обнаружил сигнал. Но я не вижу никаких плюсов такой стратегии.

Другие вопросы по тегам