如何使线程等到变量到达.NET / Csharp中的一组值之一

时间:2011-10-18 14:00:23

标签: c# multithreading asynchronous system.reactive

我编写了一个类,其属性类型为TProperty,类型为Action<TProperty>的事件会在该属性发生更改时触发。 TProperty通常是枚举类型,但这无关紧要。

我想创建一个带签名的方法

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

阻止调用它的线程,直到属性更改为AllowedValues中的值。当然如果在属性被AllowedValues中的某个属性被调用时调用WaitUntilPropertyIs,则应该没有阻塞。 WaitUntilPropertyIs最多等待TimeoutMs,如果超出超时(正常AutoResetEvent.Wait语义),则返回false。

我愿意使用Reactive Extensions或传统的同步构造。

5 个答案:

答案 0 :(得分:4)

对于这种情况,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;
    }
}

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

答案 1 :(得分:4)

在Rx + ReactiveUI中,这可以通过以下方式完成:

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

如果我们想添加超时+一个表示该属性实际设置的bool,我们可以这样做:

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

答案 2 :(得分:1)

我决定将所有这些功能包装在一个类中,以便我可以在其他地方重用它。这段代码可以解决问题。

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

答案 3 :(得分:0)

'Waiter'类和列表式容器应该这样做。锁定列表访问和属性setter / event /具有相同Critical Section的任何内容。服务员类只需要AllowedValues和一个让调用者等待的事件,(好吧,可能是'bool check(IEnumerable newAllowedValues)'帮助者)。

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

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

RGDS, 马丁

答案 4 :(得分:0)

将该属性建模为Behavior(Rx中的类,以及在一段时间内更改的值的反应式编程中的一般概念)。