简体   繁体   English

5秒后取消长时间运行的任务

[英]Cancel long running Task after 5 seconds when not finished

I have created a task which creates an XML string. 我创建了一个创建XML字符串的任务。 The task can last multiple seconds. 该任务可以持续几秒钟。 When the task isn't finished after 5 seconds, I want to cancel the Task 'smootly' and continue with writing the rest of the XML. 如果5秒钟后任务仍未完成,我想“迅速”取消任务,然后继续编写其余的XML。 So I built in cancellation inside my task. 因此,我在任务中内置了取消功能。 But although I see the following message in the Log: 但是,尽管我在日志中看到以下消息:

  • ProcessInformationTask timed out ProcessInformationTask超时

I also see this line in my log 我也在日志中看到这一行

Adding the process information took 10001 ms 添加过程信息花费了10001毫秒

I wonder why this can happen because I want to cancel the Task after 5 seconds (if not finished). 我想知道为什么会发生这种情况,因为我想在5秒钟后取消任务(如果尚未完成)。 So I expect the task to last 5 seconds max. 因此,我希望任务最多持续5秒。 How can I solve this? 我该如何解决? Probably the cancelation isn't setup properly? 取消操作可能设置不正确?

Code where I call my task 我调用任务的代码

string additionalInformation = null;
var contextInfo = new StringBuilder();

var xmlWriterSettings = new XmlWriterSettings()
{
    OmitXmlDeclaration = true,
    ConformanceLevel = ConformanceLevel.Fragment
};

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;

using (XmlWriter xmlWriter = XmlWriter.Create(contextInfo, xmlWriterSettings))
{
    try
    {
        xmlWriter.WriteStartElement("AdditionalInformation");

        //Write xml (not long running)

        var watch = System.Diagnostics.Stopwatch.StartNew();
        string processInformation = AddProcessesInformation(xmlWriterSettings);
        watch.Stop();
        var elapsedMs = watch.ElapsedMilliseconds;
        Log.Info("Adding the process information took : " + elapsedMs + " ms");

        if (!string.IsNullOrEmpty(processInformation))
        {
            xmlWriter.WriteRaw(processInformation);
        }

        //Write xml (not long running)

        xmlWriter.WriteEndElement();

        additionalInformation = contextInfo.ToString();
    }

    catch (Exception e)
    {
        Log.Info("An exception occured during writing the additional information: " + e.Message);
        return false;
    }

    return true;
} 

Task method 任务方法

private static string AddProcessesInformation(XmlWriterSettings xmlWriterSettings)
    {
        var tokenSource = new CancellationTokenSource();
        var token = tokenSource.Token;
        var contextInfo = new StringBuilder();

        var processInformationTask = Task<string>.Factory.StartNew(() =>
        {
            if (token.IsCancellationRequested)
            {
                Log.Info("Cancellation request for the ProcessInformationTask");
            }

            Thread.Sleep(10000);

            return "Ran without problems'";

        }, token);

        if (!processInformationTask.Wait(5000, token))
        {
            Log.Info("ProcessInformationTask timed out");
            tokenSource.Cancel();
        }

        return processInformationTask?.Result;
    }

I think you should check if cancellation is requested multiple Times, after each step in your method. 我认为您应该在方法的每个步骤之后检查是否多次要求取消。 Here is example using for loop: 这是使用for循环的示例:

    private static string AddProcessesInformation(XmlWriterSettings xmlWriterSettings)
    {
        var tokenSource = new CancellationTokenSource();
        var token = tokenSource.Token;
        var contextInfo = new StringBuilder();

        var processInformationTask = Task<string>.Factory.StartNew(() =>
        {               
            for(int i = 0; i < 10; i++)
            {
                if (token.IsCancellationRequested)
                {
                    Console.WriteLine("Cancellation request for the ProcessInformationTask");
                    return string.Empty;
                }
                Thread.Sleep(1000);
            }

            return "Ran without problems'";

        }, token);

        if (!processInformationTask.Wait(5000, token))
        {
            Console.WriteLine("ProcessInformationTask timed out");
            tokenSource.Cancel();
        }

        return processInformationTask?.Result;
    }

If inside task method you call other methods, you can pass cancellation token to them and use method token.ThrowIfCancellationRequested() inside. 如果在任务方法内部调用其他方法,则可以将取消令牌传递给它们,并在内部使用方法token.ThrowIfCancellationRequested()

        var processInformationTask = Task<string>.Factory.StartNew(() =>
        {
            try
            {
                Method1(token);
                Thread.Sleep(1000);
                Method2(token);
                Thread.Sleep(1000);
            }
            catch(OperationCanceledException ex)
            {
                return string.Empty;
            }
            return "Ran without problems'";

        }, token);


    private static void Method1(CancellationToken token)
    {
        token.ThrowIfCancellationRequested();
        // do more work
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM