我需要做的是等待执行特定方法。
class MyClass
{
public void TheMethodToAwait()
{
// Do something to signal that the method was invoked
}
public AnAwaitableObject TheMethodToAwaitWasExecuted()
{
// Need help in creating the awaitable object
}
}
正如您所看到的,我想要一个返回等待对象的方法,以便其他类可以执行以下操作:
await InstanceOfMyClass.TheMethodToAwaitWasExecuted();
我知道创建自定义等待的基础知识,但我正在努力解决这个问题,因为我从未在实践中这样做过。
像往常一样,非常感谢任何帮助,谢谢
附加信息
需要多次等待该方法。实际上它是游戏主循环中的更新。我的异步方法做了一些必须与循环同步的工作。如果游戏以60 FPS运行,则异步方法需要每秒执行60次。希望现在更有意义。
我最终如何解决
为了完整起见,我也将发布我的实现:
public class AwaitableEvent<TResult>
{
private ConcurrentQueue<EventAwaiter<TResult>> _awaiterQueue;
public AwaitableEvent()
{
_awaiterQueue = new ConcurrentQueue<EventAwaiter<TResult>>();
}
public EventAwaiter<TResult> GetAwaiter()
{
var awaiter = new EventAwaiter<TResult>();
_awaiterQueue.Enqueue(awaiter);
return awaiter;
}
public void Notify(TResult result)
{
var awaiterCount = _awaiterQueue.Count;
while (awaiterCount-- > 0)
{
EventAwaiter<TResult> awaiter;
if (_awaiterQueue.TryDequeue(out awaiter))
{
awaiter.Notify(result);
}
}
}
}
public class EventAwaiter<TResult> : INotifyCompletion
{
private Action _continuation;
private TResult _result;
public bool IsCompleted
{
get { return false; }
}
public EventAwaiter()
{
}
public void Notify(TResult result)
{
_result = result;
_continuation();
}
public void OnCompleted(Action continuation)
{
_continuation = continuation;
}
public TResult GetResult()
{
return _result;
}
}
答案 0 :(得分:1)
如果它只是一个信号(不是可以“设置”的东西,然后再“取消设置”和“再次设置”),那么你可以使用TaskCompletionSource<T>
:
class MyClass
{
private readonly TaskCompletionSource<object> _tcs = new TaskCompletionSource<object>();
public void TheMethodToAwait()
{
_tcs.TrySetResult(null);
}
public Task TheMethodToAwaitWasExecutedAsync()
{
return _tcs.Task;
}
}
答案 1 :(得分:1)
由于你需要一些可以“取消设置”和“重置”的东西,你真正需要的是asynchronous equivalent of a manual-reset event。您可以构建自己的或use one from my AsyncEx library:
class MyClass
{
private readonly AsyncManualResetEvent _mre = new AsyncManualResetEvent();
public void TheMethodToAwait()
{
_mre.Set();
}
public Task TheMethodToAwaitWasExecutedAsync()
{
return _mre.WaitAsync();
}
// TODO: some code that calls _mre.Reset()
}
答案 2 :(得分:1)
现在我更了解你在做什么,我可以推荐一种模拟系统的方法。我想要一种编写可以“等待”下一个模拟更新周期的脚本的方法,这非常类似于游戏循环中的帧。我最初使用的是TaskCompletionSource,但由于在每次等待时都构造了这个,所以重载是有问题的。我最终创建了一个这样的自定义awaiter:
实现'always-incomplete'等待的简单基类(基本上只是一系列连续的触发):
public abstract class Awaiter : INotifyCompletion
{
private readonly List<Action> _continuations = new List<Action>();
public bool IsCompleted
{
get { return false; }
}
public abstract void GetResult();
public void OnCompleted(Action continuation)
{
lock (_continuations)
{
var syncContext = SynchronizationContext.Current;
if (syncContext != null)
{
_continuations.Add(() => syncContext.Send(s => continuation(), null));
}
else
{
_continuations.Add(continuation);
}
}
}
public void RunContinuations()
{
Action[] continuations;
lock (_continuations)
{
continuations = _continuations.ToArray();
_continuations.Clear();
}
foreach (var continuation in continuations)
{
continuation();
}
}
public Awaiter GetAwaiter()
{
return this;
}
}
模拟课程内部就是这个(你可以await CycleExecutedEvent()
使用它):
private readonly CycleExecutedAwaiter _awaiter = new CycleExecutedAwaiter();
public Awaiter CycleExecutedEvent()
{
if (!IsRunning) throw new TaskCanceledException("Simulation has been stopped");
return _awaiter;
}
private sealed class CycleExecutedAwaiter : Awaiter
{
public override void GetResult()
{
var syncContext = SynchronizationContext.Current as ScriptingSynchronizationContext;
if (syncContext != null && syncContext.StopRequested)
throw new TaskCanceledException("Stop Requested");
}
}
希望这与您的用例相似,以便提供帮助。
重要提示:
此实现包含一些您可能想要删除的同步上下文技巧。我需要控制用于脚本编写的线程,并且还希望阻止我的更新循环,同时给脚本执行时间。您的要求可能不同;例如,您可能希望使用Task.Run异步运行continuation。但这应该至少为如何开始提供一个框架。