异步Web服务调用并非始终异步 [英] Async Web Service call not consistently asynchronous

查看:82
本文介绍了异步Web服务调用并非始终异步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用带有ASP.NET Web API 2的任务并行库创建异步Web服务时遇到问题.我对方法 StartAsyncTest 进行了异步调用,并创建了取消令牌来中止方法.我在全局存储令牌,然后检索它并从第二个方法 CancelAsyncTest 调用它.这是代码:

I'm having issues creating an asynchronous web service using the Task Parallel Library with ASP.NET Web API 2. I make an asynchronous call to a method StartAsyncTest and create a cancellation token to abort the method. I store the token globally and then retrieve it and call it from a second method CancelAsyncTest. Here is the code:

// Private Global Dictionary to hold text search tokens
private static Dictionary<string, CancellationTokenSource> TextSearchLookup
    = new Dictionary<string, CancellationTokenSource>();

/// <summary>
/// Performs an asynchronous test using a Cancellation Token
/// </summary>
[Route("StartAsyncTest")]
[HttpGet]
public async Task<WsResult<long>> StartAsyncTest(string sSearchId)
{
    Log.Debug("Method: StartAsyncTest; ID: " + sSearchId + "; Message: Entering...");

    WsResult<long> rWsResult = new WsResult<long>
    {
        Records = -1
    };

    try
    {
        var rCancellationTokenSource = new CancellationTokenSource();
        {
            var rCancellationToken = rCancellationTokenSource.Token;

            // Set token right away in TextSearchLookup
            TextSearchLookup.Add("SyncTest-" + sSearchId, rCancellationTokenSource);

            HttpContext.Current.Session["SyncTest-" + sSearchId] =
                rCancellationTokenSource;

            try
            {
                // Start a New Task which has the ability to be cancelled 
                var rHttpContext = (HttpContext)HttpContext.Current;

                await Task.Factory.StartNew(() =>
                {
                    HttpContext.Current = rHttpContext;

                    int? nCurrentId = Task.CurrentId;

                    StartSyncTest(sSearchId, rCancellationToken);

                }, TaskCreationOptions.LongRunning);
            }
            catch (OperationCanceledException e)
            {
                Log.Debug("Method: StartAsyncText; ID: " + sSearchId
                    + "; Message: Cancelled!");
            }
        }
    }
    catch (Exception ex)
    {
        rWsResult.Result = "ERROR";
        if (string.IsNullOrEmpty(ex.Message) == false)
        {
            rWsResult.Message = ex.Message;
        }
    }

    // Remove token from Dictionary
    TextSearchLookup.Remove(sSearchId);
    HttpContext.Current.Session[sSearchId] = null;
    return rWsResult;
}

private void StartSyncTest(string sSearchId, CancellationToken rCancellationToken)
{
    // Spin for 1100 seconds
    for (var i = 0; i < 1100; i++)
    {
        if (rCancellationToken.IsCancellationRequested)
        {
            rCancellationToken.ThrowIfCancellationRequested();
        }

        Log.Debug("Method: StartSyncTest; ID: " + sSearchId
            + "; Message: Wait Pass #" + i + ";");

        Thread.Sleep(1000);
    }

    TextSearchLookup.Remove("SyncTest-" + sSearchId);

    HttpContext.Current.Session.Remove("SyncTest-" + sSearchId);
}

[Route("CancelAsyncTest")]
[HttpGet]
public WsResult<bool> CancelAsyncTest(string sSearchId)
{
    Log.Debug("Method: CancelAsyncTest; ID: " + sSearchId
        + "; Message: Cancelling...");

    WsResult<bool> rWsResult = new WsResult<bool>
    {
        Records = false
    };

    CancellationTokenSource rCancellationTokenSource =
        (CancellationTokenSource)HttpContext.Current.Session["SyncTest-" + sSearchId];

    // Session doesn't always persist values. Use TextSearchLookup as backup
    if (rCancellationTokenSource == null)
    {
        rCancellationTokenSource = TextSearchLookup["SyncTest-" + sSearchId];
    }

    if (rCancellationTokenSource != null)
    {
        rCancellationTokenSource.Cancel();

        TextSearchLookup.Remove("SyncTest-" + sSearchId);
        HttpContext.Current.Session.Remove("SyncTest-" + sSearchId);

        rWsResult.Result = "OK";
        rWsResult.Message = "Cancel delivered successfully!";
    }
    else
    {
        rWsResult.Result = "ERROR";
        rWsResult.Message = "Reference unavailable to cancel task"
            + " (if it is still running)";
    }

    return rWsResult;
}

将其部署到IIS后,第一次调用 StartAsyncTest ,然后调用 CancelAsyncTest (通过REST端点)时,两个请求均通过,并且按预期取消.但是,第二次, CancelAsyncTest 请求只是挂起,并且仅在 StartAsyncTest 完成(1100秒后)之后才调用该方法.我不知道为什么会这样. StartAsyncTest 似乎在被调用一次之后就劫持了所有线程.感谢任何人都能提供的帮助!

After I deploy this to IIS, the first time I call StartAsyncTest and then CancelAsyncTest (via the REST endpoints), both requests go through and it cancels as expected. However, the second time, the CancelAsyncTest request just hangs and the method is only called after StartAsyncTest completes (after 1100 seconds). I don't know why this occurs. StartAsyncTest seems to highjack all threads after it's called once. I appreciate any help anyone can provide!

推荐答案

同事向 Task.Factory.StartNew (在 StartAsyncTest 中)提供了替代呼叫:

A colleague offered a alternative call to Task.Factory.StartNew (within StartAsyncTest):

await Task.Factory.StartNew(() =>
{
    StartSyncTest(sSearchId, rCancellationToken);
},
rCancellationToken, 
TaskCreationOptions.LongRunning,
TaskScheduler.FromCurrentSynchronizationContext());

此实现似乎解决了异步问题.现在,将来对 CancelAsyncTest 的调用将成功,并按预期取消该任务.

This implementation seemed to solve the asynchronous issue. Now future calls to CancelAsyncTest succeed and cancel the task as intended.

这篇关于异步Web服务调用并非始终异步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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