如何使线程等待,直到一个变量达到一组值在.net / csharp之一



我写了一个类,它具有类型为TProperty的属性和类型为Action<TProperty>的事件,每当该属性更改时触发。TProperty是典型的枚举类型,但这并不重要。

我想创建一个签名为 的方法
bool WaitUntilPropertyIs(int TimeoutMs, IEnumerable<TProperty> AllowedValues) 

阻塞调用它的线程,直到属性更改为AllowedValues中的值。当然,如果WaitUntilPropertyIs被调用时,属性已经是AllowedValues之一,不应该有阻塞。WaitUntilPropertyIs应该最多等待TimeoutMs,如果超时超过(正常的AutoResetEvent.Wait语义),则返回false。

我对使用响应式扩展或传统同步结构持开放态度。

Rx对于这种场景来说是多余的。您可以通过ManualResetEventManualResetEventSlim进行操作。您可以按照以下方式对解决方案进行模式设置:

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;
    }
}

我不知道你的确切需求,所以考虑修改上面的代码来防止竞争条件

在Rx + ReactiveUI中,这是通过:

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

如果我们想添加一个timeout +一个bool值,表示该属性已经被设置,我们可以这样做:

bool valueIsSet = someObject.ObservableForProperty(x => x.SomeProperty)
    .Where(x => x.Value == "Something")
    .Select(x => true)
    .Timeout(TimeSpan.FromSeconds(5), Observable.Return(false))
    .First();

我决定将所有这些功能包装在一个类中,以便我可以在其他地方重用它。下面的代码完成了这个任务。

在构造函数中,传入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
}

应该由一个'Waiter'类和一个列表样式的容器来完成。锁定列表访问和具有相同临界区的属性setter/event等。服务员类只需要AllowedValues和一个事件供调用者等待(好吧,可能是一个'bool check(IEnumerable newAllowedValues)' helper)。

应该是这样。在WaitUntilPropertyIs()中,获取CS并首先检查属性值,看看它是否可以释放CS并立即返回,如果不能,调用者必须等待。创建一个服务员,复制传入的AllowedValues,将服务员添加到列表中,释放CS并等待传入超时的事件。当事件等待返回时,重新获取CS,从列表中删除服务员,dispose()它,释放CS并从事件等待调用返回true/false。

在setter/Action/whatever中,获取CS,迭代列表,与新的AllowedValues进行比较,在新值满足范围的任何侍者上触发任何事件,然后释放CS。

祝好,马丁

将该属性建模为Behavior(来自Rx的类,以及响应式编程中用于一段时间内变化的值的一般概念)

相关内容

  • 没有找到相关文章