Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 是否有一种模式或推荐的方法来等待通用值以实现特定值?_C#_.net_Asynchronous_Async Await - Fatal编程技术网

C# 是否有一种模式或推荐的方法来等待通用值以实现特定值?

C# 是否有一种模式或推荐的方法来等待通用值以实现特定值?,c#,.net,asynchronous,async-await,C#,.net,Asynchronous,Async Await,在这个崭新的异步世界中,我发现自己一次又一次地需要一种等待的方法来等待某些东西处于某种状态或满足某种条件。例如(伪代码) wait(myStateMachine.State==StateEnum.Ready) 等待(myDownloadProgress==100.0) wait(myspideviefifolevel!=0) 出现这些情况是因为我需要推迟一些异步启动的代码,直到代码的另一部分达到某种状态。例如,用户启动UI的新部分,但后台线程仍在尝试与硬件建立通信。或者,控制一个硬件的状态机需要

在这个崭新的异步世界中,我发现自己一次又一次地需要一种等待的方法来等待某些东西处于某种状态或满足某种条件。例如(伪代码)

wait(myStateMachine.State==StateEnum.Ready)

等待(myDownloadProgress==100.0)

wait(myspideviefifolevel!=0)

出现这些情况是因为我需要推迟一些异步启动的代码,直到代码的另一部分达到某种状态。例如,用户启动UI的新部分,但后台线程仍在尝试与硬件建立通信。或者,控制一个硬件的状态机需要等待,直到另一个控制另一个硬件的状态机达到某种就绪状态

我已经提出了实现这一点的主要奇妙和古怪的方法,在这样做的过程中,我注意到某些模式正在出现,因此自然的进展是为我们编写一些帮助类/泛型代码,以可重用的方式执行这类行为

在我走这条路之前,肯定还有其他人在解决这类问题,所以我很感兴趣的是,是否有人知道一种经过测试的模式或推荐的方法。我在WWW上做了一些搜索,但没有找到任何特别有说服力的东西。这涉及到这个问题,但op要求的是另一个原因。这要求同样的事情,但具体到任务进度

到目前为止,我是如何做到这一点的 1.别这样!使用事件 当我控制正在变化的源(例如,状态机的状态)时,我经常说服自己我做错了,我应该让生产者(状态机)在我的条件达到时生成一个事件,而不是等待实现一个值。然后,任何侦听器都可以使用
AutoResetEvent
ManualResetEvent
等待处理程序

{
  myStateMachine.OnMyConditionAchieved += OnConditionAchievedEventHandler;
  myEvent = new AutoResetEvent(false);
  myEvent.WaitOne();
}

void OnConditionAchievedEventHandler(object sender, EventArgs e)
{
  myEvent.Set();
}
这样做的缺点是,我真的不想把生产者代码与特定于消费者需求的事件混为一谈

2.使用事件、编码开销与性能权衡 如果(1)中还没有一个方便的事件,那么制作人将永远被修改以满足消费者的需求。因此,明显的自然过程是使用类似于
INotifyPropertyChanged
模式的东西。这样,生产者就不会无限延伸,消费者也会这样做:

void StateMachine_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  if (e.Property == "State")
  {
    if (myStateMachine.State == State.TheStateThatIWant)
    {
      myEvent.Set();
    }
  }
}
这感觉像是一场胜利,因为我经常使用NotifyPropertyChanged系统-数据绑定需要它,所以要添加的代码更少,但是,我们正在监听制作人的每一个更改以过滤出想要的条件,这感觉很肮脏-肯定有更好的方法吗

3.使用任务和轮询(ugh) 启动一个任务,检查状态并在条件无限期未满足或任务取消之前休眠。然后等待任务完成。当条件满足或取消时,任务完成

优点-使代码简洁,特别是在使用Task.Run(()=>…)lambda方法时,可以利用通常也需要的任务取消技术(令牌、超时等) 反对者-投票感觉很肮脏,似乎有点笨手笨脚地构建了一个全新的任务来完成这么简单的工作

4.使用任务并等待事件 总比投票好吧?但与1)和2)的问题相同,需要一个适当的事件来挂钩,因此2)(INotifyPropertyChanged)比1)更常见。因此,实现通常以启动任务、等待ManualResetEvent、侦听PropertyChanged并过滤更改、激发事件、从任务返回结束

5.圣杯 我不是百分之百肯定,但事实上 1) 轻量级 2) 允许在启动等待时指定条件 3) 如果有10000件东西等待着不同的房产来实现特定的价值,这将不会是一个巨大的资源负担 4) 清洁,即正确处置资源

MagicValueWaiter waitForValue = new MagicValueWaiter(MyStateMachine, nameof(State), (s) => (s > 4) && (s < 8));
await waitForInit.WaitAsync();
基本上,是一个泛型类/方法,用于等待给定对象的给定属性或字段以lambda返回bool的形式满足给定条件

这种方法乍一看可能暗示了一种轮询技术,但是,如果我强制MyObject遵守类似“必须实现INotifyPropertyChanged”或一些自定义基类来支持这种行为(例如ISupportValueWaiting),那么我们可以挂接到一些常见的行为,例如MyObject上的事件,并避免轮询


有什么明显的解决办法我没找到吗?有人对如何做这件事有什么新奇的想法吗?或者我的评论?

你要找的是。

你能做的就是使用
TaskCompletionSource
和接口
INotifyPropertyChanged
来完成
任务,只要
obj
上的某些条件得到满足

因此:

公共静态类
{
公共静态任务WaitForAsync(此T对象,字符串属性名称,Func pred)
其中T:INotifyPropertyChanged
{
obj=obj??抛出新ArgumentNullException(nameof(obj));
PropertyName=PropertyName??抛出新的ArgumentNullException(nameof(PropertyName));
pred=pred??抛出新ArgumentNullException(nameof(pred));
var taskCompletionsource=新的taskCompletionsource();
无效处理程序(对象发送方,PropertyChangedEventArgs e)
{
if(e.PropertyName==PropertyName&&pred(obj))
{
obj.PropertyChanged-=处理程序;
taskCompletionsource.SetResult(true);
}
}
obj.PropertyChanged+=处理程序;
返回taskCompletionsource.Task;
}
}
你可以像这样使用它:

wait someValue.WaitForAsync(nameof(SomeType.SomeProperty),s=>…);
你遇到过吗?遇到过吗
await ValueWaiter.WaitAsync(MyObject, nameof(MyPropertyorField), (s) => (s == States.Init);