如何使用timout取消TaskCompletionSource [英] How to cancel a TaskCompletionSource using a timout

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

问题描述

我有我异步调用使用功能等待关键字:

 公共任务< StatePropertyEx> RequestStateForEntity(的EntityKey实体,字符串作为propName)
{
    VAR TCS =新TaskCompletionSource<&将stateInfo GT;();
    尝试
    {
        VAR propInstance = BuildCacheKey(实体,作为propName);
        StateCacheItem cacheItem;
        如果(_stateCache.TryGetValue(propInstance,出cacheItem))
        {
            tcs.SetResult(新将stateInfo(cacheItem.State.Name,cacheItem.State.Value));
            返回tcs.Task;
        }        //在本地缓存中没有找到状态,以便保存后,TCS和要求状态
        VAR cacheKey = BuildCacheKey(实体,作为propName);
       _stateRequestItemList.TryAdd(cacheKey,新StateRequestItem(实体,作为propName,TCS));        _evtClient.SubmitStateRequest(实体,作为propName);        返回tcs.Task;
    }
    赶上(异常前)
    {
        tcs.SetException(除息);
        返回tcs.Task;
    }
}

该函数具有一看,看它是否具有它需要的信息和如果它不会返回它。如果它不具有的细节它发送一个请求出的最终应进来作为事件。在这一点上我的code(未显示)发现存储TaskCompletionSource项目,将结果并返回。这一切工作正常,但现在我已经被要求考虑在哪里,当我通过_evtClient.SubmitStateRequest(实体,作为propName);申请状态的回复可能永远无法返回的情况一致。我需要实现某种超时机制,所以我可以取消TCS任务,以便调用函数可以优雅地失败。我一直在SO和互联网上寻找并不能找到任何看起来合适的。现在我不知道我是否需要调整上述code以不同的方式。任何人都可以建议或点我到类似的情景?

在code调用上面的函数可以调用它在一个单一的打击是这样的:

  VAR stateProperty =等待RequestStateForEntity(键,Statename的);

或批处理,这样的:

 等待
    Task.WhenAll(
        stateDefinitions.Select(stateDefinition => stateDefinition.Name)
            。选择(
                Statename的= GT;
                    Task.Factory.StartNew(
                        异步()=> results.Add(的await RequestStateForEntity(键,Statename的)))
                        .Unwrap())
            .ToArray());


解决方案

首先,你真正要启用的的取消的。 ,取消来自超时时,这只是一个注脚。

.NET有一定的伟大内置注销和基于任务的异步模式prescribes如何使用它

从本质上讲,你想要做的是采取的CancellationToken

 任务< StatePropertyEx> RequestStateForEntity(的EntityKey实体,字符串作为propName,
    的CancellationToken的CancellationToken);

接下来,你要当令牌信号做出反应。理想情况下,你会想只通过的CancellationToken 下至 _evtClient ,这样的要求是真正取消:

  _evtClient.SubmitStateRequest(实体,作为propName,的CancellationToken);

这是落实取消以正常的方式,它的伟大工程,如果 SubmitStateRequest 已取消的理解。通常情况下,事件的参数有一个标志,指示取消(例如,<一个href=\"http://msdn.microsoft.com/en-us/library/system.componentmodel.asynccompletedeventargs.cancelled(v=vs.110).aspx\"><$c$c>AsyncCompletedEventArgs.Cancelled).如果可能的话,使用这种方法(即,将 _evtClient 在必要时支持取消)。

但有时这恰恰是不可能的。在这种情况下,您可以选择的 pretend 的支持取消。你在做什么的真正的做的只是忽略请求,如果它完成它被取消了。这不是最理想的情况,但有时你别无选择。

就个人而言,我真的不喜欢这种方法,因为它使API谎言:方法签名宣称支持取消,但它真的只是假装它。因此,首先,我建议这个记录。放在一个code评论道歉,并解释说, _evtClient 不支持取消,而取消其实只是pretend取消。

然后,你需要挂接到的CancellationToken 自己,以后国家要求产品在列表中,但实际的请求被发送之前:

  VAR项目=新StateRequestItem(实体,作为propName,TCS);
_stateRequestItemList.TryAdd(cacheKey,项目);
item.CancellationRegistration = cancellationToken.Register(()=&GT;
{
  StateRequestItem cancelledItem;
  如果(!_stateRequestItemList.TryRemove(cacheKey,出cancelledItem))
    返回;
  cancelledItem.TaskCompletionSource.TrySetCanceled();
});
_evtClient.SubmitStateRequest(实体,作为propName);

最后,你需要更新你的事件处理程序完成code(未显示)忽略其中状态请求项目已被删除的情况,并处置 CancellationRegistration 如果国家要求的项目被发现。


一旦你的方法支持取消,则很容易通过一个定时器来取消:

  VAR CTS =新CancellationTokenSource(TimeSpan.FromSeconds(10));
的CancellationToken令牌= cts.Token;

或其他任何一种情况。也就是说,如果用户取消无论他(她)在做什么。或者,如果系统的另一部分决定它不需要该数据了。一旦你的code支持消除,它可以处理取消的任何理由。

I have the function that I call asynchronously using the await key word:

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;
    }
}

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);

or in a batch, like this:

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 has some great built-in support for cancellation, and the Task-based Asynchronous Pattern prescribes how to use it.

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

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

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);

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.

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.

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);

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.

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

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