如何使用超时取消TaskCompletionSource [英] How to cancel a TaskCompletionSource using a timeout

查看:167
本文介绍了如何使用超时取消TaskCompletionSource的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我具有使用await关键字异步调用的功能:

I have the function that I call asynchronously using the await keyword:

public Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName)
{
    var tcs = new TaskCompletionSource<StateInfo>();
    try
    {
        var propInstance = BuildCacheKey(entity, propName);
        StateCacheItem cacheItem;
        if (_stateCache.TryGetValue(propInstance, out cacheItem))
        {
            tcs.SetResult( new StateInfo (cacheItem.State.Name, cacheItem.State.Value) );
            return tcs.Task;
        }

        //state not found in local cache so save the tcs for later and request the state
        var cacheKey = BuildCacheKey(entity, propName);
       _stateRequestItemList.TryAdd(cacheKey, new StateRequestItem(entity, propName, tcs));

        _evtClient.SubmitStateRequest(entity, propName);

        return tcs.Task;
    }
    catch (Exception ex)
    {
        tcs.SetException(ex);
        return tcs.Task;
    }
}

该函数可以查看它是否具有所需的信息,是否返回所需的信息.如果没有详细信息,它将发出一个请求,该请求最终应作为事件进入.到那时,我的代码(未显示)找到存储的TaskCompletionSource项,设置结果并返回.一切正常,但是现在我被要求考虑一种情况,当我通过"_evtClient.SubmitStateRequest(entity,propName);"请求状态时,可能永远不会返回答复.线.我需要实现某种超时机制,以便取消TCS任务,以便函数调用方可以正常退出.我一直在寻找SO和互联网,却找不到任何合适的内容.现在,我不确定是否需要以其他方式重组上述代码.谁能建议或指出我类似的情况?

The function has a look to see if it has the information it needs and if it does it returns it. If it doesn’t have the details it sends a request out which should eventually come in as an event. At that point my code (not shown) finds the stored TaskCompletionSource item, sets the result and returns it. This all works fine but I have now been asked to consider a situation where a reply may never be returned when I request state via the "_evtClient.SubmitStateRequest(entity, propName);" line. I need to implement some sort of timeout mechanism so I can cancel the TCS task so the function caller can fail gracefully. I’ve been looking on SO and the internet and can’t find anything that looks right. I’m now not sure if I need to restructure the above code in a different way. Can anyone advise or point me to a similar scenario?

调用上述函数的代码可以像这样单击一次:

The code that calls the above function can call it in a single hit like this:

var stateProperty = await RequestStateForEntity(key, stateName);

或像这样的批处理:

await
    Task.WhenAll(
        stateDefinitions.Select(stateDefinition => stateDefinition.Name)
            .Select(
                stateName =>
                    Task.Factory.StartNew(
                        async () => results.Add(await RequestStateForEntity(key, stateName)))
                        .Unwrap())
            .ToArray());

推荐答案

首先,您真正要启用的功能是取消.取消来自超时的事实只是一个脚注.

First off, what you really want to enable is cancellation. The fact that the cancellation comes from a timeout is just a footnote.

.NET具有一些对取消的强大内置支持,并且基于任务的异步模式规定了如何使用它.

.NET has some great built-in support for cancellation, and the Task-based Asynchronous Pattern prescribes how to use it.

基本上,您要做的是获取一个 CancellationToken :

Essentially, what you want to do is take a CancellationToken:

Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName,
    CancellationToken cancellationToken);

接下来,您希望在发出该令牌信号时做出响应.理想情况下,您只想将 CancellationToken 传递给 _evtClient ,以便真正取消该请求:

Next, you want to respond when that token is signaled. Ideally, you would want to just pass the CancellationToken down to the _evtClient so that the request is truly cancelled:

_evtClient.SubmitStateRequest(entity, propName, cancellationToken);

这是实现取消的常规方法,如果 SubmitStateRequest 已经了解取消功能,则效果很好.通常,事件参数具有指示取消的标志(例如, AsyncCompletedEventArgs.Cancelled ).如果可能的话,请使用这种方法(即,根据需要更改 _evtClient 以支持取消).

This is the normal way of implementing cancellation, and it works great if SubmitStateRequest already understands cancellation. Often the event arguments have a flag indicating cancellation (e.g., AsyncCompletedEventArgs.Cancelled). If at all possible, use this approach (i.e., change _evtClient as necessary to support cancellation).

但是有时候这是不可能的.在这种情况下,您可以选择假装以支持取消.您实际上所做的只是忽略请求,如果该请求在取消后完成了.这不是最理想的情况,但有时您别无选择.

But sometimes this just isn't possible. In this case, you can choose to pretend to support cancellation. What you're actually doing is just ignoring the request if it completes after it was cancelled. This is not the most ideal situation but sometimes you have no choice.

就我个人而言,我真的不喜欢这种方法,因为它使API成为谎言":方法签名声称支持取消,但实际上只是在伪造它.因此,首先,我建议对此进行记录.放入代码注释道歉,解释 _evtClient 不支持取消,而取消"实际上只是假装取消.

Personally, I don't really like this kind of approach since it makes the API "lie": the method signature claims to support cancellation but it really is just faking it. So first, I recommend documenting this. Put in a code comment apology explaining that _evtClient doesn't support cancellation, and the "cancellation" is actually just pretend cancellation.

然后,您需要在状态请求项位于列表中之后但在发送实际请求之前,自行挂钩 CancellationToken :

Then, you'll need to hook into the CancellationToken yourself, after the state request item is in the list but before the actual request is sent:

var item = new StateRequestItem(entity, propName, tcs);
_stateRequestItemList.TryAdd(cacheKey, item);
item.CancellationRegistration = cancellationToken.Register(() =>
{
  StateRequestItem cancelledItem;
  if (!_stateRequestItemList.TryRemove(cacheKey, out cancelledItem))
    return;
  cancelledItem.TaskCompletionSource.TrySetCanceled();
});
_evtClient.SubmitStateRequest(entity, propName);

最后,您需要更新事件处理程序完成代码(未显示),以忽略状态请求项已被删除的情况,如果状态请求,则处置 CancellationRegistration 找到了项目.

Finally, you'll need to update your event handler completion code (not shown) to ignore the situation where the state request item has already been removed, and to dispose the CancellationRegistration if the state request item is found.

一旦您的方法支持取消,那么就很容易通过计时器取消:

Once your method supports cancellation, then it's easy to cancel via a timer:

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
CancellationToken token = cts.Token;

或其他任何情况.说,如果用户取消了他正在做的事情.或者,如果系统的另一部分决定不再需要该数据.一旦您的代码支持取消,它就可以以任何理由处理取消.

or from any other kind of situation. Say, if the user cancels whatever (s)he's doing. Or if another part of the system decides it doesn't need that data anymore. Once your code supports cancellation, it can handle cancellation for any reason.

这篇关于如何使用超时取消TaskCompletionSource的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆