c# – 高效信令任务,用于经常重新安置事件的TPL完成

c# – 高效信令任务,用于经常重新安置事件的TPL完成,第1张

概述我正在研究一个模拟系统,除其他外,它允许在离散的模拟时间步骤中执行任务.执行都发生在模拟线程的上下文中,但是,从使用系统的“运算符”的角度来看,它们希望异步地运行.值得庆幸的是,TPL和方便的’async / await’关键字使得这一点非常简单.我在Simulation上有一个原始方法,如下所示: public Task CycleExecutedEvent() { lo 我正在研究一个模拟系统,除其他外,它允许在离散的模拟时间步骤中执行任务.执行都发生在模拟线程的上下文中,但是,从使用系统的“运算符”的角度来看,它们希望异步地运行.值得庆幸的是,TPL和方便的’async / await’关键字使得这一点非常简单.我在Simulation上有一个原始方法,如下所示:
public Task CycleExecutedEvent()    {        lock (_cycleExecutedbroker)        {            if (!IsRunning) throw new TaskCanceledException("Simulation has been stopped");            return _cycleExecutedbroker.RegisterForCompletion(CycleExecutedEventname);        }    }

这基本上是创建一个新的taskcompletionsource,然后返回一个Task.此任务的目的是在模拟发生新的“ExecuteCycle”时执行其继续.

然后我有一些像这样的扩展方法:

public static async Task WaitForDuration(this ISimulation simulation,double duration)    {        double startTime = simulation.CurrentSimulatedTime;        do        {            await simulation.CycleExecutedEvent();        } while ((simulation.CurrentSimulatedTime - startTime) < duration);    }    public static async Task WaitForCondition(this ISimulation simulation,Func<bool> condition)    {        do        {            await simulation.CycleExecutedEvent();        } while (!condition());    }

这些非常方便,从“ *** 作符”的角度构建序列,根据条件采取行动并等待模拟时间段.我遇到的问题是CycleExecuted频繁发生(如果我以完全加速的速度运行,大约每隔几毫秒).因为这些’wait’辅助方法在每个循环中注册一个新的’await’,这会导致taskcompletionsource实例中的大量转换.

我已经分析了我的代码,并且我发现大约5.5%的总cpu时间花费在这些完成中,其中“活动”代码中只花费了可忽略不计的百分比.实际上,所有时间都花在注册新的完成上,同时等待触发条件有效.

我的问题:我如何在保持async / await模式的便利性的同时提高写入“运算符行为”的性能?我认为我需要像轻量级和/或可重复使用的taskcompletionsource这样的东西,因为触发事件经常发生.

我一直在做更多的研究,听起来一个很好的选择是创建Awaitable模式的自定义实现,它可以直接绑定到事件,消除了对一堆taskcompletionsource和Task实例的需要.它在这里有用的原因是有很多不同的延续等待CycleExecutedEvent,他们需要经常等待它.理想情况下,我正在寻找一种方法来排队延续回调,然后在事件发生时回调队列中的所有内容.我会继续挖掘,但如果人们知道干净的方法,我欢迎任何帮助.

对于将来浏览这个问题的人来说,这是我放在一起的定制等待者:

public sealed class CycleExecutedAwaiter : INotifyCompletion{    private Readonly List<Action> _continuations = new List<Action>();    public bool IsCompleted    {        get { return false; }    }    public voID GetResult()    {    }    public voID OnCompleted(Action continuation)    {        _continuations.Add(continuation);    }    public voID runcontinuations()    {        var continuations = _continuations.ToArray();        _continuations.Clear();        foreach (var continuation in continuations)            continuation();    }    public CycleExecutedAwaiter GetAwaiter()    {        return this;    }}

在模拟器中:

private Readonly CycleExecutedAwaiter _cycleExecutedAwaiter = new CycleExecutedAwaiter();    public CycleExecutedAwaiter CycleExecutedEvent()    {        if (!IsRunning) throw new TaskCanceledException("Simulation has been stopped");        return _cycleExecutedAwaiter;    }

这有点好笑,因为等待者从未报告完整,但火灾继续称为完成注册;不过,它适用于这个应用程序.这将cpu开销从5.5%降低到2.1%.它可能仍然需要一些调整,但它比原来的改进很好.

解决方法 await关键字不仅适用于任务,它适用于遵循等待模式的任何事物.有关详细信息,请参阅 Stephen Toub’s article await anything;.

短版本是该类型必须具有方法GetAwaiter()返回一个实现INotifyCompletion类型和还IsCompleted属性和getResult()方法(空隙返回,如果AWAIT表达不应该有一个值).有关示例,请参见TaskAwaiter.

如果你创建自己的等待,你可以每次返回相同的对象,避免分配许多taskcompletionsources的开销.

总结

以上是内存溢出为你收集整理的c# – 高效信令任务,用于经常重新安置事件的TPL完成全部内容,希望文章能够帮你解决c# – 高效信令任务,用于经常重新安置事件的TPL完成所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1246101.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存