Added some statistics to coroutines

It took me half a year, but closes #4
This commit is contained in:
Ell 2020-12-20 17:12:43 +01:00
parent 607a1587fa
commit d3b34154ad
5 changed files with 69 additions and 20 deletions

View file

@ -1,14 +1,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
namespace Coroutine { namespace Coroutine {
/// <summary> /// <summary>
/// A reference to a currently running coroutine. /// A reference to a currently running coroutine.
/// This is returned by <see cref="CoroutineHandler.Start(IEnumerator{Wait})"/>. /// This is returned by <see cref="CoroutineHandler.Start(IEnumerator{Wait},string)"/>.
/// </summary> /// </summary>
public class ActiveCoroutine { public class ActiveCoroutine {
private readonly IEnumerator<Wait> enumerator; private readonly IEnumerator<Wait> enumerator;
private readonly Stopwatch stopwatch;
private Wait current; private Wait current;
/// <summary> /// <summary>
@ -21,13 +23,35 @@ namespace Coroutine {
/// </summary> /// </summary>
public bool WasCanceled { get; private set; } public bool WasCanceled { get; private set; }
/// <summary> /// <summary>
/// The total amount of time that <see cref="MoveNext"/> took.
/// This is the amount of time that this active coroutine took for the entirety of its "steps", or yield statements.
/// </summary>
public TimeSpan TotalMoveNextTime { get; private set; }
/// <summary>
/// The total amount of times that <see cref="MoveNext"/> was invoked.
/// This is the amount of "steps" in your coroutine, or the amount of yield statements.
/// </summary>
public int MoveNextCount { get; private set; }
/// <summary>
/// The average amount of time that <see cref="MoveNext"/> took.
/// This is the average amount of time that each "step", or each yield statement, of this coroutine took to execute.
/// </summary>
public TimeSpan AverageMoveNextTime => new TimeSpan(this.TotalMoveNextTime.Ticks / this.MoveNextCount);
/// <summary>
/// An event that gets fired when this active coroutine finishes or gets cancelled. /// An event that gets fired when this active coroutine finishes or gets cancelled.
/// When this event is called, <see cref="IsFinished"/> is always true. /// When this event is called, <see cref="IsFinished"/> is always true.
/// </summary> /// </summary>
public event FinishCallback OnFinished; public event FinishCallback OnFinished;
/// <summary>
/// The name of this coroutine.
/// When not specified on startup of this coroutine, the name defaults to an empty string.
/// </summary>
public readonly string Name;
internal ActiveCoroutine(IEnumerator<Wait> enumerator) { internal ActiveCoroutine(IEnumerator<Wait> enumerator, string name, Stopwatch stopwatch) {
this.enumerator = enumerator; this.enumerator = enumerator;
this.stopwatch = stopwatch;
this.Name = name;
} }
/// <summary> /// <summary>
@ -60,7 +84,13 @@ namespace Coroutine {
} }
internal bool MoveNext() { internal bool MoveNext() {
if (!this.enumerator.MoveNext()) { this.stopwatch.Restart();
var result = this.enumerator.MoveNext();
this.stopwatch.Stop();
this.TotalMoveNextTime += this.stopwatch.Elapsed;
this.MoveNextCount++;
if (!result) {
this.IsFinished = true; this.IsFinished = true;
this.OnFinished?.Invoke(this); this.OnFinished?.Invoke(this);
return false; return false;

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Coroutine { namespace Coroutine {
/// <summary> /// <summary>
@ -11,19 +10,24 @@ namespace Coroutine {
private static readonly CoroutineHandlerInstance Instance = new CoroutineHandlerInstance(); private static readonly CoroutineHandlerInstance Instance = new CoroutineHandlerInstance();
/// <inheritdoc cref="CoroutineHandlerInstance.Start(IEnumerable{Wait})"/> /// <inheritdoc cref="CoroutineHandlerInstance.TickingCount"/>
public static ActiveCoroutine Start(IEnumerable<Wait> coroutine) { public static int TickingCount => Instance.TickingCount;
return Instance.Start(coroutine); /// <inheritdoc cref="CoroutineHandlerInstance.EventCount"/>
public static int EventCount => Instance.EventCount;
/// <inheritdoc cref="CoroutineHandlerInstance.Start(IEnumerable{Wait},string)"/>
public static ActiveCoroutine Start(IEnumerable<Wait> coroutine, string name = "") {
return Instance.Start(coroutine, name);
} }
/// <inheritdoc cref="CoroutineHandlerInstance.Start(IEnumerator{Wait})"/> /// <inheritdoc cref="CoroutineHandlerInstance.Start(IEnumerator{Wait},string)"/>
public static ActiveCoroutine Start(IEnumerator<Wait> coroutine) { public static ActiveCoroutine Start(IEnumerator<Wait> coroutine, string name = "") {
return Instance.Start(coroutine); return Instance.Start(coroutine, name);
} }
/// <inheritdoc cref="CoroutineHandlerInstance.InvokeLater"/> /// <inheritdoc cref="CoroutineHandlerInstance.InvokeLater"/>
public static void InvokeLater(Wait wait, Action action) { public static ActiveCoroutine InvokeLater(Wait wait, Action action) {
Instance.InvokeLater(wait, action); return Instance.InvokeLater(wait, action);
} }
/// <inheritdoc cref="CoroutineHandlerInstance.Tick"/> /// <inheritdoc cref="CoroutineHandlerInstance.Tick"/>

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
namespace Coroutine { namespace Coroutine {
@ -11,24 +12,36 @@ namespace Coroutine {
private readonly List<ActiveCoroutine> tickingCoroutines = new List<ActiveCoroutine>(); private readonly List<ActiveCoroutine> tickingCoroutines = new List<ActiveCoroutine>();
private readonly List<ActiveCoroutine> eventCoroutines = new List<ActiveCoroutine>(); private readonly List<ActiveCoroutine> eventCoroutines = new List<ActiveCoroutine>();
private readonly Stopwatch stopwatch = new Stopwatch();
/// <summary>
/// The amount of <see cref="ActiveCoroutine"/> instances that are currently waiting for a tick (waiting for time to pass)
/// </summary>
public int TickingCount => this.tickingCoroutines.Count;
/// <summary>
/// The amount of <see cref="ActiveCoroutine"/> instances that are currently waiting for an <see cref="Event"/>
/// </summary>
public int EventCount => this.eventCoroutines.Count;
/// <summary> /// <summary>
/// Starts the given coroutine, returning a <see cref="ActiveCoroutine"/> object for management. /// Starts the given coroutine, returning a <see cref="ActiveCoroutine"/> object for management.
/// Note that this calls <see cref="IEnumerable{T}.GetEnumerator"/> to get the enumerator. /// Note that this calls <see cref="IEnumerable{T}.GetEnumerator"/> to get the enumerator.
/// </summary> /// </summary>
/// <param name="coroutine">The coroutine to start</param> /// <param name="coroutine">The coroutine to start</param>
/// <param name="name">The name that this coroutine should have. Defaults to an empty string.</param>
/// <returns>An active coroutine object representing this coroutine</returns> /// <returns>An active coroutine object representing this coroutine</returns>
public ActiveCoroutine Start(IEnumerable<Wait> coroutine) { public ActiveCoroutine Start(IEnumerable<Wait> coroutine, string name = "") {
return this.Start(coroutine.GetEnumerator()); return this.Start(coroutine.GetEnumerator(), name);
} }
/// <summary> /// <summary>
/// Starts the given coroutine, returning a <see cref="ActiveCoroutine"/> object for management. /// Starts the given coroutine, returning a <see cref="ActiveCoroutine"/> object for management.
/// </summary> /// </summary>
/// <param name="coroutine">The coroutine to start</param> /// <param name="coroutine">The coroutine to start</param>
/// <param name="name">The name that this coroutine should have. Defaults to an empty string.</param>
/// <returns>An active coroutine object representing this coroutine</returns> /// <returns>An active coroutine object representing this coroutine</returns>
public ActiveCoroutine Start(IEnumerator<Wait> coroutine) { public ActiveCoroutine Start(IEnumerator<Wait> coroutine, string name = "") {
var inst = new ActiveCoroutine(coroutine); var inst = new ActiveCoroutine(coroutine, name, this.stopwatch);
if (inst.MoveNext()) { if (inst.MoveNext()) {
if (inst.IsWaitingForEvent()) { if (inst.IsWaitingForEvent()) {
this.eventCoroutines.Add(inst); this.eventCoroutines.Add(inst);
@ -45,8 +58,9 @@ namespace Coroutine {
/// </summary> /// </summary>
/// <param name="wait">The wait to wait for</param> /// <param name="wait">The wait to wait for</param>
/// <param name="action">The action to execute after waiting</param> /// <param name="action">The action to execute after waiting</param>
public void InvokeLater(Wait wait, Action action) { /// <returns>An active coroutine object representing this coroutine</returns>
this.Start(InvokeLaterImpl(wait, action)); public ActiveCoroutine InvokeLater(Wait wait, Action action) {
return this.Start(InvokeLaterImpl(wait, action));
} }
/// <summary> /// <summary>

View file

@ -9,7 +9,7 @@ namespace Test {
private static readonly Event TestEvent = new Event(); private static readonly Event TestEvent = new Event();
public static void Main() { public static void Main() {
var seconds = CoroutineHandler.Start(WaitSeconds()); var seconds = CoroutineHandler.Start(WaitSeconds(), "Awesome Waiting Coroutine");
CoroutineHandler.Start(PrintEvery10Seconds(seconds)); CoroutineHandler.Start(PrintEvery10Seconds(seconds));
CoroutineHandler.Start(EmptyCoroutine()); CoroutineHandler.Start(EmptyCoroutine());
@ -50,6 +50,7 @@ namespace Test {
Console.WriteLine("The time is " + DateTime.Now); Console.WriteLine("The time is " + DateTime.Now);
if (first.IsFinished) { if (first.IsFinished) {
Console.WriteLine("By the way, the first coroutine has finished!"); Console.WriteLine("By the way, the first coroutine has finished!");
Console.WriteLine($"{first.Name} data: {first.MoveNextCount} moves, {first.TotalMoveNextTime} total time, {first.AverageMoveNextTime} average");
Environment.Exit(0); Environment.Exit(0);
} }
} }

View file

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Test</RootNamespace> <RootNamespace>Test</RootNamespace>
<AssemblyName>Test</AssemblyName> <AssemblyName>Test</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">