В идеале мне нужно запустить задачу, не дожидаясь ее, и я ожидаю, что через некоторое время она выдаст исключение, имитирующее таймаут.
Моя проблема в том, что я не могу изменить способ вызова методов в основном потоке и могу просто добавлять туда методы.
Я также знаю что исключения, возникающие в асинхронных задачах, которые не ожидаются, никогда не перехватываются в основном потоке. Но это именно то, что мне нужно, и я не могу найти решение.
Чтобы проиллюстрировать мою проблему, приведу следующий код:
Код: Выделить всё
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(() => 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 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(Func task) =>
taskFactory
.StartNew(task)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
Я также пробовал использовать 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;
}
}
Есть есть ли способ вызвать исключение в основном потоке из асинхронной задачи, не дожидаясь его?
Подробнее здесь: https://stackoverflow.com/questions/786 ... -some-time