C# - 使用异步任务在一段时间后抛出异常

问题描述 投票:0回答:1

我正在尝试创建一个异步运行的强制超时机制。 理想情况下,我需要运行一个任务,而不需要等待它,并且我希望在一段时间后它会抛出一个异常来模拟超时。

我的问题是我无法更改主线程中调用方法的方式,只能在其中添加方法。

我还知道,在未等待的异步任务中引发的异常永远不会在主线程中捕获。但这正是我所需要的,但找不到解决方案。

为了举例说明我的问题,请使用以下代码:

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

        namespace ConsoleApp1
        {
            class Program
            {
                //Main method which I don't have control over, to change how other methods are called, I can only add methods here.
                static void Main(string[] args)
                {
                    Console.WriteLine("Hello World!");
                    try
                    {
                        //Forced timeout
                        Console.Write("Init timeout \n");
                        var forcedTimeout = myForcedTimeoutMethod(1000);

                        //Do something that might be long
                        //Just pretending the main thread is still doing some work that should take longer than 1 second
                        AsyncUtil.RunSync<bool>(() => ForcedTimeoutContext.Wait(2000));


                        Console.Write("Method finished \n");
                        forcedTimeout.Dispose();


                    }
                    catch (Exception e)
                    {
                        Console.Write("Exception caught: " + e.Message);
                    }
                }

                public static ForcedTimeoutContext myForcedTimeoutMethod(int TimeInMs)
                {

                    ForcedTimeoutContext forcedTimeout = new ForcedTimeoutContext();
                    forcedTimeout.TokenSource = new CancellationTokenSource();
        
                    forcedTimeout.Task = Task.Run(async delegate
                    {

                        await Task.Delay(TimeSpan.FromMilliseconds(TimeInMs), forcedTimeout.TokenSource.Token);
                        forcedTimeout.TokenSource.Token.ThrowIfCancellationRequested();
                        throw new Exception("Forced timeout reached");
                    });

                    return forcedTimeout;
                }
            }

            //Context class to be able to Dispose and cancel the timeout if needed
            class ForcedTimeoutContext : IDisposable
            {
                public CancellationTokenSource TokenSource;
                public Task Task;

                public void Dispose()
                {
                    if (TokenSource != null)
                        TokenSource.Dispose();
                    if (Task != null)
                        Task.Dispose();
                }

                public static async Task<bool> Wait(int ssms)
                {
                    await Task.Delay(ssms);
                    return true;
                }
            }

            //Utility helper to run async methods synchronously
            public class AsyncUtil
            {
                private static readonly TaskFactory taskFactory = new TaskFactory(
                    CancellationToken.None,
                    TaskCreationOptions.None,
                    TaskContinuationOptions.None,
                    TaskScheduler.Default);

                public static TResult RunSync<TResult>(Func<Task<TResult>> task) =>
                    taskFactory
                        .StartNew(task)
                        .Unwrap()
                        .GetAwaiter()
                        .GetResult();
            }
        }

正如预期的那样,“myForcedTimeoutMethod”函数中的任务不会等待,“强制超时达到”异常永远不会被主线程捕获。

我也尝试使用 Task.Run().ContinueWith(...) ,但延续也在不同的范围内执行。

我能够实现的最接近的解决方案是使用 Thread.Interrupt() ,如下所示:

                public static  ForcedTimeoutContext myForcedTimeoutMethod(int TimeInMs)
                {

                    ForcedTimeoutContext forcedTimeout = new ForcedTimeoutContext();
                    forcedTimeout.TokenSource = new CancellationTokenSource();
                    var currThread = Thread.CurrentThread;

                    forcedTimeout.Task = Task.Run(async delegate
                   {

                       await Task.Delay(TimeSpan.FromMilliseconds(TimeInMs), forcedTimeout.TokenSource.Token);
                       currThread.Interrupt();
                   });

                    return forcedTimeout;
                }
            }

但是,在这种情况下,我对异常没有任何控制权,我希望能够抛出特定的异常。

有没有办法在异步任务的主线程中抛出异常而不等待它?

c# multithreading exception
1个回答
0
投票

你不能随便在不同的线程中引发异常。这个现实(至少部分)是为什么

CancellationToken
存在于
async
代码中,这是一种跨流中断的协作形式 - 但它要求您想要能够中断的代码采取额外的步骤来允许自身被中断(通过预检查或委托回调)。

这里没有你想要编织的魔法。

© www.soinside.com 2019 - 2024. All rights reserved.