C# Web API和取消令牌

C# Web API和取消令牌,c#,asp.net,asp.net-web-api,C#,Asp.net,Asp.net Web Api,我试图让一个Web API和取消令牌一起工作,但由于某些原因,它似乎不能很好地发挥作用 这是我的Web API代码。它包含取消令牌属性和方法 public class TokenCancellationApiController : ApiController { private static CancellationTokenSource cTokenSource = new CancellationTokenSource(); // Create a cancellation

我试图让一个Web API和取消令牌一起工作,但由于某些原因,它似乎不能很好地发挥作用

这是我的Web API代码。它包含取消令牌属性和方法

public class TokenCancellationApiController : ApiController
{
    private static CancellationTokenSource cTokenSource = new CancellationTokenSource();
    // Create a cancellation token from CancellationTokenSource
    private static CancellationToken cToken = cTokenSource.Token;
    // Create a task and pass the cancellation token

    [HttpGet]
    public string BeginLongProcess()
    {
        string returnMessage = "The running process has finished!";
        try
        {
            LongRunningFunc(cToken, 6);
        }
        catch (OperationCanceledException cancelEx)
        {
            returnMessage = "The running process has been cancelled.";
        }
        finally
        {
            cTokenSource.Dispose();
        }
        return returnMessage;
    }

    [HttpGet]
    public string CancelLongProcess()
    {
        // cancelling task
        cTokenSource.Cancel();
        return "Cancellation Requested";
    }

    private static void LongRunningFunc(CancellationToken token, int seconds)
    {
        Console.WriteLine("Long running method");
        for (int j = 0; j < seconds; j++)
        {
            Thread.Sleep(1000);
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("Cancellation observed.");
                throw new OperationCanceledException(token); // acknowledge cancellation
            }
        }
        Console.WriteLine("Done looping");
    }
}
公共类令牌取消ApiController:ApiController
{
私有静态CancellationTokenSource cTokenSource=新的CancellationTokenSource();
//从CancellationTokenSource创建取消令牌
私有静态取消令牌cToken=cTokenSource.Token;
//创建任务并传递取消令牌
[HttpGet]
公共字符串BeginLongProcess()
{
string returnMessage=“正在运行的进程已完成!”;
尝试
{
龙润宁Func(cToken,6);
}
捕获(操作取消异常取消)
{
returnMessage=“正在运行的进程已被取消。”;
}
最后
{
cTokenSource.Dispose();
}
返回消息;
}
[HttpGet]
公共字符串进程()
{
//取消任务
cTokenSource.Cancel();
返回“请求取消”;
}
私有静态void LongRunningFunc(取消令牌令牌,整数秒)
{
WriteLine(“长时间运行方法”);
对于(int j=0;j
我有以下HTML代码:

<script>
    function BeginLongProcess()
    {
        alert("Will now send AJAX request to start long 6 second process...");
        $.ajax({
            url: "/api/TokenCancellationApi/BeginLongProcess",
            type: "GET",
            dataType: 'json',
            success: function (result) {
                alert(result);
            },
            error: function (xhr, status, error) {
                var err = eval("(" + xhr.responseText + ")");
                console.error(err.Message)
            }
        });
    }
    function CancelLongProcess() {
        $.ajax({
            url: "/api/TokenCancellationApi/CancelLongProcess",
            type: "GET",
            dataType: 'json',
            success: function (result) {
                alert(result);
            },
            error: function (xhr, status, error) {
                var err = eval("(" + xhr.responseText + ")");
                console.error(err.Message)
            }
        });
    }
</script>

<form id="aForm" runat="server">
    <p>
        <button type="button" onclick="BeginLongProcess()">Begin Long Process</button>
    </p>
    <p>
        <button type="button" onclick="CancelLongProcess()">Cancel Long Process</button>
    </p>
</form>

函数BeginLongProcess()
{
警报(“现在将发送AJAX请求以启动长6秒的进程…”);
$.ajax({
url:“/api/TokenCancellationApi/BeginLongProcess”,
键入:“获取”,
数据类型:“json”,
成功:功能(结果){
警报(结果);
},
错误:函数(xhr、状态、错误){
var err=eval(“+xhr.responseText+”);
控制台错误(错误消息)
}
});
}
函数进程(){
$.ajax({
url:“/api/TokenCancellationApi/CancelLongProcess”,
键入:“获取”,
数据类型:“json”,
成功:功能(结果){
警报(结果);
},
错误:函数(xhr、状态、错误){
var err=eval(“+xhr.responseText+”);
控制台错误(错误消息)
}
});
}

开始漫长的过程

取消长流程

Web API方法调用得很好。 当我单击按钮开始长流程,然后单击“取消”时,我希望它取消长流程,并返回一条警告消息,告诉我该流程已取消

但事实并非如此。虽然有一个令牌取消请求,但它似乎没有注册,并且长时间的进程一直运行到完成为止


有人能告诉我为什么它不能像我希望的那样工作吗?

您的线程睡眠是确定的,因为当您单击“取消”时,这样的线程不会醒来。此外,若请求取消,则应在迭代期间进行检查。由于已将令牌源设置为静态,因此一次只能运行一个长时间运行的调用。这就是为什么在开始长时间运行的流程之前,您还必须检查一个流程是否已经启动。添加了必要的锁以确保实例正确同步

稍微修改了代码,但工作正常。使其运行于配置的迭代中以方便测试。另外,睡眠时间增加到5秒。根据需要进行更改

如果您想要异步运行长时间运行的方法,这也会起作用。在begin方法中取消注释注释代码

public class TokenCancellationApiController : ApiController
{
    private static object _lock = new object();
    public static string _lastError;

    // Static types will mean that you can only run 
    // one long running process at a time.
    // If more than 1 needs to run, you will have to 
    // make them instance variable and manage 
    // threading and lifecycle
    private static CancellationTokenSource cTokenSource;       
    private static CancellationToken cToken;

    [HttpGet]
    [Route("api/TokenCancellationApi/BeginLongProcess/{seconds}")]
    public string BeginLongProcess(int seconds)
    {
        //Lock and check if process has already started or not.
        lock (_lock)
        {
            if (null != cTokenSource)
            {
                return "A long running is already underway.";
            }
            cTokenSource = new CancellationTokenSource();
        }

        //if running asynchronously
        //var task = Task.Factory.StartNew(() => LongRunningFunc(cTokenSource.Token, seconds));
        //task.ContinueWith(Cleanup);
        //return "Long running process has started!";

        //if running synchronusly
        try
        {
            LongRunningFunc(cTokenSource.Token, seconds);            
        }
        catch(OperationCanceledException)
        {
            return "The running process has been cancelled";
        }
        catch(Exception ex)
        {
            _lastError = ex.Message;
            return ex.Message;
        }
        finally
        {
            Cleanup(null);
        }
        return "Long running process has completed!";

    }

    [HttpGet]
    public string CancelLongProcess()
    {
        // cancelling task
        if (null != cTokenSource)
        {
            lock (_lock)
            {
                if (null != cTokenSource)
                {
                    cTokenSource.Cancel();
                }
                return "Cancellation Requested";
            }
        }
        else
        {
            return "Long running task already completed";
        }
    }

    [HttpGet]
    public string GetLastError()
    {
        return (string.IsNullOrEmpty(_lastError)) ? "No Error" : _lastError;
    }

    private static void Cleanup(Task task)
    {
        if (null != task && task.IsFaulted)
        {
            System.Diagnostics.Debug.WriteLine("Error encountered while running task");
            _lastError = task.Exception.GetBaseException().Message;
        }

        lock (_lock)
        {
            if (null != cTokenSource)
            {
                cTokenSource.Dispose();
            }
            cTokenSource = null;
        }
    }

    private static void LongRunningFunc(CancellationToken token, int seconds)
    {
        System.Diagnostics.Debug.WriteLine("Long running method");
        int j = 0;

        //Long running loop should always check if cancellation requested.
        while(!token.IsCancellationRequested && j < seconds)            
        {
            //Wait on token instead of deterministic sleep
            //This way, thread will wakeup as soon as canellation
            //is requested even if sleep time hasn't elapsed.
            //Waiting 5 seconds
            token.WaitHandle.WaitOne(5000);
            j++;
        }

        if (token.IsCancellationRequested)
        {
            throw new OperationCanceledException();
        }

        System.Diagnostics.Debug.WriteLine("Done looping");
    }
}
公共类令牌取消ApiController:ApiController
{
私有静态对象_lock=新对象();
公共静态字符串\u lastError;
//静态类型将意味着您只能运行
//一次一个长时间运行的进程。
//如果需要运行超过1个,则必须
//使它们成为实例变量并进行管理
//线程和生命周期
私有静态取消令牌源cTokenSource;
私有静态取消令牌;
[HttpGet]
[路由(“api/TokenCancellationApi/BeginLongProcess/{seconds}”)]
公共字符串BeginLongProcess(整数秒)
{
//锁定并检查进程是否已启动。
锁
{
如果(null!=cTokenSource)
{
return“长时间运行已经开始了。”;
}
cTokenSource=新的CancellationTokenSource();
}
//如果异步运行
//var task=task.Factory.StartNew(()=>LongRunningFunc(cTokenSource.Token,秒));
//任务。继续(清理);
//return“长时间运行的进程已启动!”;
//如果同步运行
尝试
{
LongRunningFunc(cTokenSource.Token,秒);
}
捕获(操作取消异常)
{
返回“正在运行的进程已被取消”;
}
捕获(例外情况除外)
{
_lastError=ex.消息;
返回ex.消息;
}
最后
{
清除(空);
}
return“长时间运行的流程已完成!”;
}
[HttpGet]
公共字符串进程()
{
//取消任务
如果(null!=cTokenSource)
{
锁
{
如果(null!=cTokenSource)
{
cTokenSource.Cancel();
}
return“取消”
<script>
    function BeginLongProcess()
    {
        alert("Will now send AJAX request to start long 6 second process...");
        var seconds = $("#seconds").val();
        $.ajax({
            url: "/api/TokenCancellationApi/BeginLongProcess/"+seconds,
            type: "GET",
            dataType: 'json',
            success: function (result) {
                alert(result);
            },
            error: function (xhr, status, error) {
                var err = eval("(" + xhr.responseText + ")");
                console.error(err.Message)
            }
        });
    }
    function CancelLongProcess() {
        $.ajax({
            url: "/api/TokenCancellationApi/CancelLongProcess",
            type: "GET",
            dataType: 'json',
            success: function (result) {
                alert(result);
            },
            error: function (xhr, status, error) {
                var err = eval("(" + xhr.responseText + ")");
                console.error(err.Message)
            }
        });
    }

    function GetLastError() {
        $.ajax({
            url: "/api/TokenCancellationApi/GetLastError",
            type: "GET",
            dataType: 'json',
            success: function (result) {
                alert(result);
            },
            error: function (xhr, status, error) {
                var err = eval("(" + xhr.responseText + ")");
                console.error(err.Message)
            }
        });
    }
    </script>


    <form id="form1" runat="server">
    <div>
    <p>
       Iterations: <input id="seconds" type="text" value="10" /> <br />
        <button type="button" onclick="BeginLongProcess()">Begin Long Process</button>
    </p>
    <p>
        <button type="button" onclick="CancelLongProcess()">Cancel Long Process</button>
    </p>

    <p>
        <button type="button" onclick="GetLastError()">Get Last Error</button>
    </p>
    </div>
    </form>
xhr.abort()