Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/289.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用超时和取消监视异步任务_C#_.net_Async Await_.net 4.5 - Fatal编程技术网

C# 使用超时和取消监视异步任务

C# 使用超时和取消监视异步任务,c#,.net,async-await,.net-4.5,C#,.net,Async Await,.net 4.5,我都 我必须监视异步任务,该任务必须是可取消的,并且执行的时间不能超过特定的生存时间 我已经知道了下面的代码 CancellationTokenSource l_cts = new CancellationTokenSource(timemillis); 它将执行取消操作(就我在异步方法中监视令牌而言)。 但是,这并没有告诉我有关他被取消的原因、超时或用户取消的任何信息。此外,超时事件被延迟,而我没有捕获到取消 Token.ThrowIfCancellationRequested(); 为了

我都

我必须监视异步任务,该任务必须是可取消的,并且执行的时间不能超过特定的生存时间

我已经知道了下面的代码

CancellationTokenSource l_cts = new CancellationTokenSource(timemillis);
它将执行取消操作(就我在异步方法中监视令牌而言)。 但是,这并没有告诉我有关他被取消的原因、超时或用户取消的任何信息。此外,超时事件被延迟,而我没有捕获到取消

Token.ThrowIfCancellationRequested();
为了解决这些问题,我编写了如下超时过程

    static async Task TestAsync(int processDelaySeconds, int cancelDelaySeconds, int timeoutDelaySeconds )
    {
        CancellationTokenSource l_cts = new CancellationTokenSource();

        // the process to monitor
        Task l_process = new Task((state) =>
        {
            Console.WriteLine("Process BEGIN");
            // dummy loop
            for (int l_i = 0; l_i != processDelaySeconds; l_i++)
            {
                Thread.Sleep(1000);
                l_cts.Token.ThrowIfCancellationRequested();
            }
            Console.WriteLine("Process END");
        }, null, l_cts.Token);

        // register timeout
        RegisteredWaitHandle l_rwh = ThreadPool.RegisterWaitForSingleObject(l_cts.Token.WaitHandle,
                (state, timedOut) =>
                {
                    if (timedOut)
                    {
                        l_cts.Cancel();
                        Console.WriteLine("Timed out");
                    }
                    else
                    {
                        Console.WriteLine("Cancel Signaled");
                    }
                },
                null, (int)TimeSpan.FromSeconds(timeoutDelaySeconds).TotalMilliseconds, true);

        // cancel task
        if (cancelDelaySeconds > 0)
        {
            Task l_cancel = new Task(() =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(cancelDelaySeconds));
                l_cts.Cancel();
            });

            l_cancel.Start();
        }

        try
        {
            l_process.Start();
            await l_process;
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Task Cancelled");
        }
        finally
        {
            // be sure to unregister the wait handle to cancel the timeout
            if (l_process.Status != TaskStatus.Canceled) l_rwh.Unregister(l_cts.Token.WaitHandle);
        }

        Console.WriteLine("Task Status is : {0}", l_process.Status);
    }  

    static async void Tests()
    {
        Console.WriteLine("NORMAL PROCESS");
        Console.WriteLine("--------------");
        await TestAsync(2, 10, 10);

        Console.WriteLine();
        Console.WriteLine("CANCEL");
        Console.WriteLine("------");
        await TestAsync(5, 2, 10);

        Console.WriteLine();
        Console.WriteLine("TIMEOUT");
        Console.WriteLine("-------");
        await TestAsync(10, 15, 2); 
    }
那么我的问题是: 幕后是否有任何缺陷或陷阱? 更好更有效的方法


ps-目标是性能,而不是更短的代码。

为了知道任务是否已被取消或超时,您可以使用
任务。WaitAny
重载需要
时间跨度

// Index will return -1 if timeout has occured, otherwise will print the index of the completed task
var cnclToken = new CancellationTokenSource().Token
var yourTask = Task.Run(() => { /* Do stuff */ }, cnclToken);
var index = Task.WhenAny(new[] { yourTask }, TimeSpan.FromSeconds(1));

为了知道您的任务是否已被取消或超时,您可以使用
任务。WaitAny
重载需要
时间跨度

// Index will return -1 if timeout has occured, otherwise will print the index of the completed task
var cnclToken = new CancellationTokenSource().Token
var yourTask = Task.Run(() => { /* Do stuff */ }, cnclToken);
var index = Task.WhenAny(new[] { yourTask }, TimeSpan.FromSeconds(1));

如果需要区分用户的取消和超时取消,可以使用
CreateLinkedTokenSource

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp
{
    internal class Program
    {
        // worker
        private static void DoWork(CancellationToken token)
        {
            for (int i = 0; i < 1000; i++)
            {
                token.ThrowIfCancellationRequested();
                Thread.Sleep(100); // do the work item
            }
            token.ThrowIfCancellationRequested();
        }

        // test
        private static void Main()
        {
            var userCt = new CancellationTokenSource();
            var combinedCt = CancellationTokenSource.CreateLinkedTokenSource(
                userCt.Token);
            combinedCt.CancelAfter(3000); // cancel in 3 seconds

            Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;
                userCt.Cancel();
            };

            var task = Task.Run(
                () => DoWork(combinedCt.Token), 
                combinedCt.Token);

            try
            {
                task.Wait();
            }
            catch (AggregateException ex)
            {
                Console.WriteLine(ex.InnerException.Message);
                if (task.IsCanceled)
                {
                    if (userCt.Token.IsCancellationRequested)
                        Console.WriteLine("Cancelled by user");
                    else if (combinedCt.Token.IsCancellationRequested)
                        Console.WriteLine("Cancelled by time-out");
                    else 
                        Console.WriteLine("Cancelled by neither user nor time-out");
                }
            }
        }
    }
}
使用系统;
使用系统线程;
使用System.Threading.Tasks;
名称空间控制台
{
内部课程计划
{
//工人
私有静态无效DoWork(CancellationToken令牌)
{
对于(int i=0;i<1000;i++)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(100);//执行工作项
}
token.ThrowIfCancellationRequested();
}
//试验
私有静态void Main()
{
var userCt=new CancellationTokenSource();
var combinedCt=CancellationTokenSource.CreateLinkedTokenSource(
userCt.Token);
combinedCt.CancelAfter(3000);//3秒内取消
Console.CancelKeyPress+=(s,e)=>
{
e、 取消=真;
userCt.Cancel();
};
var task=task.Run(
()=>DoWork(组合CT.Token),
组合ct.Token);
尝试
{
task.Wait();
}
捕获(聚合异常)
{
Console.WriteLine(例如InnerException.Message);
如果(task.IsCanceled)
{
if(userCt.Token.IsCancellationRequested)
Console.WriteLine(“由用户取消”);
else if(combinedCt.Token.IsCancellationRequested)
控制台写入线(“因超时而取消”);
其他的
Console.WriteLine(“用户或超时均未取消”);
}
}
}
}
}

至于您的原始代码,您确实不需要
ThreadPool.RegisterWaitForSingleObject(l_cts.Token.WaitHandle,…)
,如果您需要区分用户的取消和超时取消,它将返回一个
IDisposable
使用,您可以使用
CreateLinkedTokenSource

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp
{
    internal class Program
    {
        // worker
        private static void DoWork(CancellationToken token)
        {
            for (int i = 0; i < 1000; i++)
            {
                token.ThrowIfCancellationRequested();
                Thread.Sleep(100); // do the work item
            }
            token.ThrowIfCancellationRequested();
        }

        // test
        private static void Main()
        {
            var userCt = new CancellationTokenSource();
            var combinedCt = CancellationTokenSource.CreateLinkedTokenSource(
                userCt.Token);
            combinedCt.CancelAfter(3000); // cancel in 3 seconds

            Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;
                userCt.Cancel();
            };

            var task = Task.Run(
                () => DoWork(combinedCt.Token), 
                combinedCt.Token);

            try
            {
                task.Wait();
            }
            catch (AggregateException ex)
            {
                Console.WriteLine(ex.InnerException.Message);
                if (task.IsCanceled)
                {
                    if (userCt.Token.IsCancellationRequested)
                        Console.WriteLine("Cancelled by user");
                    else if (combinedCt.Token.IsCancellationRequested)
                        Console.WriteLine("Cancelled by time-out");
                    else 
                        Console.WriteLine("Cancelled by neither user nor time-out");
                }
            }
        }
    }
}
使用系统;
使用系统线程;
使用System.Threading.Tasks;
名称空间控制台
{
内部课程计划
{
//工人
私有静态无效DoWork(CancellationToken令牌)
{
对于(int i=0;i<1000;i++)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(100);//执行工作项
}
token.ThrowIfCancellationRequested();
}
//试验
私有静态void Main()
{
var userCt=new CancellationTokenSource();
var combinedCt=CancellationTokenSource.CreateLinkedTokenSource(
userCt.Token);
combinedCt.CancelAfter(3000);//3秒内取消
Console.CancelKeyPress+=(s,e)=>
{
e、 取消=真;
userCt.Cancel();
};
var task=task.Run(
()=>DoWork(组合CT.Token),
组合ct.Token);
尝试
{
task.Wait();
}
捕获(聚合异常)
{
Console.WriteLine(例如InnerException.Message);
如果(task.IsCanceled)
{
if(userCt.Token.IsCancellationRequested)
Console.WriteLine(“由用户取消”);
else if(combinedCt.Token.IsCancellationRequested)
控制台写入线(“因超时而取消”);
其他的
Console.WriteLine(“用户或超时均未取消”);
}
}
}
}
}

至于您的原始代码,您确实不需要
ThreadPool.RegisterWaitForSingleObject(l_cts.Token.WaitHandle,…)
,但是,对于这一点,它会返回一个
IDisposable
,可以与
using

一起使用,谢谢您的评论,而我们可以使用Task.WhenAny{mytask,Task.Delay(ms)}当时间参数为on时,没有过载或扩展。你的相关链接是关于WaitAny的,它返回一个int,不可等待。WhenAny方法在调用2任务时产生结果,这是对资源的浪费(我相信,但可能是错误的)。谢谢你的时间。为什么这是浪费资源?WaitAny将等待任务或TimeSpan完成,只有一个任务运行您的代码。不过,感谢您的评论,虽然我们可以使用Task来完成此操作。WhenAny{mytask,Task.Delay(ms)}WhenAny上没有时间参数的重载或扩展。您的相关链接是关于WaitAny的,返回哪个