Как заставить поток ждать, пока переменная не достигнет одного из значений в .NET / Csharp - PullRequest
3 голосов
/ 18 октября 2011

Я написал класс со свойством типа TProperty и событием типа Action<TProperty>, которое срабатывает при каждом изменении этого свойства. TProperty обычно является типом enum, но это не должно иметь значения.

Я бы хотел создать метод с подписью

bool WaitUntilPropertyIs(int TimeoutMs, IEnumerable<TProperty> AllowedValues) 

, который блокирует вызывающий его поток, пока свойство не изменится на значение в AllowedValues. Конечно, если WaitUntilPropertyIs вызывается, когда свойство уже считано одним из AllowedValues, не должно быть никаких блокировок. WaitUntilPropertyIs должен ждать не более TimeoutMs и возвращать значение false, если превышено время ожидания (обычная AutoResetEvent.Wait семантика).

Я открыт для использования Reactive Extensions или традиционных конструкций синхронизации.

Ответы [ 5 ]

4 голосов
/ 18 октября 2011

Rx является избыточным для такого сценария. Вы можете сделать это через ManualResetEvent или ManualResetEventSlim. Вы можете сопоставить свое решение со следующим:

public event Action<TProperty> MyEvent;
public TProperty Prop { get; private set; }

bool WaitUntilPropertyIs(int timeout, IEnumerable<TProperty> allowedValues)
{
    var gotAllowed = new ManualResetEventSlim(false);
    Action<int> handler = item =>
    {
        if (allowedValues.Contains(item)) gotAllowed.Set();
    };

    try
    {
        MyEvent += handler;
        return allowedValues.Contains(Prop) || gotAllowed.Wait(timeout);
    }
    finally
    {
        MyEvent -= handler;
    }
}

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

4 голосов
/ 18 октября 2011

В Rx + ReactiveUI это выполняется с помощью:

someObject.ObservableForProperty(x => x.SomeProperty)
    .Where(x => x.Value == "Something")
    .First();

Если мы хотим добавить тайм-аут + бул, означающий, что свойство действительно установлено, мы можем сделать это:

bool valueIsSet = someObject.ObservableForProperty(x => x.SomeProperty)
    .Where(x => x.Value == "Something")
    .Select(x => true)
    .Timeout(TimeSpan.FromSeconds(5), Observable.Return(false))
    .First();
1 голос
/ 19 октября 2011

Я решил обернуть все эти функции в классе, чтобы я мог использовать их в других местах. Этот код делает свое дело.

В конструкторе передайте CurrentValueFunc, который должен вернуть текущее значение отслеживаемой переменной по требованию, передайте IsValueAcceptableFunc, который должен вернуть true, если текущее значение является приемлемым.

Необходимо убедиться, что ValueWatcher.ValueUpdated вызывается при каждом изменении значения.

public class ValueWatcher<TValue> : IDisposable
{
    ManualResetEvent _ev = new ManualResetEvent(false);
    Func<TValue, bool> _isValueAcceptableFunc;

    public ValueWatcher(Func<TValue> CurrentValueFunc, Func<TValue, bool> IsValueAcceptableFunc)
    {
        _isValueAcceptableFunc = IsValueAcceptableFunc;
        ValueUpdated(CurrentValueFunc.Invoke());
    }

    public void ValueUpdated(TValue Value)
    {
        if (_isValueAcceptableFunc.Invoke(Value))
            _ev.Set();
        else
            _ev.Reset();
    }

    public bool Wait()
    {
        return _ev.WaitOne();
    }

    public bool Wait(int TimeoutMs)
    {
        return _ev.WaitOne(TimeoutMs);
    }

    public bool Wait(TimeSpan ts)
    {
        return _ev.WaitOne(ts);
    }

    #region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
    }

    void Dispose(bool Disposing)
    {
        if (Disposing)
        {
            _ev.Dispose();
        }
    }

    #endregion
}
0 голосов
/ 19 октября 2011

Смоделируйте это свойство как Behavior (класс из Rx и общая концепция реактивного программирования для значений, изменяющихся в течение определенного периода времени).

0 голосов
/ 18 октября 2011

Класс 'Waiter' и контейнер в стиле списка должны это делать. Заблокируйте доступ к списку и установщик свойств / событие / что-либо с тем же критическим разделом. Классу-официанту нужны только AllowedValues ​​и событие для ожидания вызывающих абонентов, (хорошо, возможно, вспомогательная проверка 'bool check (IEnumerable newAllowedValues)').

Так и должно быть. В WaitUntilPropertyIs () получите CS и сначала проверьте значения свойств, чтобы увидеть, может ли он просто освободить CS и немедленно вернуться, если нет, то вызывающая сторона должна ждать. Создайте официанта, скопируйте в переданные значения AllowedValues, добавьте официанта в список, отпустите CS и дождитесь события с прошедшим таймаутом. Когда ожидание события возвращается, повторно запишите CS, удалите официанта из списка, удалите его (), отпустите CS и вернитесь с true / false из вызова ожидания события.

В установщике / Action / что-нибудь, заполучите CS, итерируйте список, сравнивая с новыми AllowedValues, запускайте любое событие на любом официанте, где новые значения удовлетворяют диапазону, и затем освобождайте CS.

Rgds, Martin

...