使用canceltoken取消异步任务

时间:2018-08-21 13:26:21

标签: c# asp.net async-await cancellationtokensource cancellation-token

我希望Web应用程序的用户能够取消服务器端长期运行的SQL查询(通过使用xhr.abort()方法)

我正在使用Response.ClientDisconnectedToken在服务器端捕获用户取消请求的事件(从这里:https://stackoverflow.com/a/17713153/8790102

SQL查询例程是通过async方法完成的(来自此处:https://stackoverflow.com/a/24834029/8790102

private async Task<DataTable> executeSelectQueryAsync(string SQL, Dictionary<string, object> BindObject = null)
{
    // This cancellationToken is tripped when the client abort the request
    CancellationToken canceltk = HttpContext.Current.Response.ClientDisconnectedToken; // Require IIS 7.5

    DataTable dt = new DataTable();

    // Only line to be logged in the server
    File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "CANCELING ENABLED" + Environment.NewLine);

    try
    {
        dt = await Task.Run(() =>
        {
            // If token is canceled, cancel SQL query
            canceltk.Register(() =>
            {
                File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "CANCELLING..." + Environment.NewLine);
                // scmd is of type OracleCommand
                if (scmd.Connection.State == ConnectionState.Open)
                    scmd.Cancel();
            });

            // Open the connection, execute the SQL command using OracleDataAdapter.Fill method and return a DataTable
            return executeSelectQuery_inner(SQL, BindObject);
        }, canceltk);
    }
    catch (TaskCanceledException ex)
    {
        try
        {
            File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "Cancelling query..." + Environment.NewLine);
            if(scmd.Connection.State == ConnectionState.Open)
                scmd.Cancel();
        }
        catch (Exception ex1)
        {
            File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "Cancel_ERROR:" + ex1.ToString() + Environment.NewLine);
        }
    }
    catch (Exception ex)
    {
        File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "OTHER EXCEPTION:" + ex.ToString() + Environment.NewLine);
    }

    return dt;
}

我的问题是,中止请求时,不会调用canceltk.Register()中注册的方法(未记录相应的文本“ CANCELLING ...”)。

实际上,根本没有记录任何文本。我不知道为什么。

如果我在调用Thread.Sleep(5000)之前使用executeSelectQueryAsync并在这5秒钟内中止了请求,那么TaskCanceledException将被成功引发并被捕获。

2 个答案:

答案 0 :(得分:1)

Task.Run计划在不受AS.NET同步上下文控制的线程池线程上执行该代码。这就是HttpContext.Current为空的原因。

此外,在这种情况下,Task.Run导致将请求的执行转移到另一个线程池线程,请求处理线程(另一个线程池线程)返回到池中,并且当{{1 }}完成执行,将线程返回到线程池线程,并检索另一个线程并填充请求处理数据。您只是使用了更多资源而没有收益。恰恰相反。

转义ASP.NET上下文的代码不应依赖它。

Oracle提供程序不支持异步操作吗?

答案 1 :(得分:0)

该方法实际上被很好地调用,但是System.Web.HttpContext.Current然后具有空值,导致未捕获到异常。

通过替换为System.Web.Hosting.HostingEnvironment.MapPath,代码可以成功运行。