3
我有一個項目,以非常相似的方式執行幾個操作(訂閱完成事件,執行任務,退出完成事件,還處理取消,超時等),所以我決定編寫處理該執行的實用程序類。但是,我遇到了一個我不明白的情況,因此我不知道如何解決。如何處理異常與明確構建的任務
這過於簡單的代碼說明了這個問題:
class Program
{
static void Main(string[] args)
{
Do();
Console.Read();
}
private static async Task Do()
{
var task = new Task(async() => await Operation()/*this throws and terminates the application*/);
try
{
await OperationExecuter.ExecuteAsync(task);
}
catch (InvalidOperationException)
{
//I expected the exception to be caught here
}
}
static async Task Operation()
{
await Task.Delay(1000);
throw new InvalidOperationException();
}
}
class OperationExecuter
{
public static async Task ExecuteAsync(Task task)
{
task.Start();
await task; //I expected the exception to be unwrapped and thrown here
}
}
我也試過有像var task = new Task(() => Operation());
任務,但異常從未處理(雖然它不終止應用程序,因爲它不是在上升主線程)。
如何正確處理異常?
更改實施採取行動的收益率相同的結果:
class Program
{
static void Main(string[] args)
{
Do();
Console.Read();
}
private static async Task Do()
{
var action = new Action(async() => await Operation() /*this throws and terminates the application*/);
try
{
await OperationExecuter.ExecuteAsync(action);
}
catch (InvalidOperationException)
{
//I expected the exception to be caught here
}
}
static async Task Operation()
{
await Task.Delay(1000);
throw new InvalidOperationException();
}
}
class OperationExecuter
{
public static async Task ExecuteAsync(Action action)
{
await Task.Run(action); //I expected the exception to be unwrapped and thrown here
}
}
對於好奇的人更現實的OperationExecuter
將沿着線的東西:
class Program
{
static void Main(string[] args)
{
Do();
Do2();
Console.Read();
}
private static async Task Do()
{
var service = new Service(new Hardware());
try
{
await
OperationExecuter.ExecuteAsync(service, handler => service.Operation1Completed += handler,
handler => service.Operation1Completed += handler, async() => await service.Operation1(),
CancellationToken.None);
}
catch (InvalidOperationException)
{
//Exception is caught!!!
}
}
private static async Task Do2()
{
var service = new Service(new Hardware());
try
{
await
OperationExecuter.ExecuteAsync(service, handler => service.Operation1Completed += handler,
handler => service.Operation1Completed += handler, async() => await service.Operation2(60),
CancellationToken.None);
}
catch (InvalidOperationException)
{
//Exception is caught!!!
}
}
}
internal class OperationExecuter
{
public static async Task ExecuteAsync(Service service, Action<EventHandler> subscriptionAction,
Action<EventHandler> unsubscriptionAction, Func<Task> sendCommandAction, CancellationToken cancellationToken)
{
var commandCompletionSource = new TaskCompletionSource<bool>();
var hardwareFailureCompletionSource = new TaskCompletionSource<bool>();
cancellationToken.Register(() => commandCompletionSource.SetCanceled());
var eventHandler = new EventHandler((sender, args) =>
{
commandCompletionSource.SetResult(true);
});
service.HardwareFailure += (sender, args) => hardwareFailureCompletionSource.SetResult(false);
subscriptionAction(eventHandler);
try
{
await Task.Run(sendCommandAction, cancellationToken);
await Task.WhenAny(commandCompletionSource.Task, hardwareFailureCompletionSource.Task);
//same for disconnection, etc
if (hardwareFailureCompletionSource.Task.IsCompleted)
{
throw new HardwareFailureException();
}
}
finally
{
unsubscriptionAction(eventHandler);
}
}
}
class HardwareFailureException : Exception
{
}
class Service
{
private readonly Hardware hardware;
public Service(Hardware hardware)
{
this.hardware = hardware;
}
public async Task Operation1() //something like sending command to hardware
{
await Task.Delay(1000);
throw new InvalidOperationException();
}
public event EventHandler Operation1Completed;
public async Task Operation2(int someParameter)
{
await Task.Delay(1000);
throw new InvalidOperationException();
}
public event EventHandler Operation2Completed;
public event EventHandler LostConnection;
public event EventHandler HardwareFailure;
}
class Hardware
{
}
'OperationExecuter'控制何時開始任務是很重要的。我想我應該真的有'OperationExecuter'採取行動,並使用'Task.Run(行動)'然後,正確? –
然後你應該改變你的實現。你不應該在寒冷的任務中傳遞信息。例如,使用'Task.Start'兩次會產生一個異常,並且也違反準則。相反,您可以將委託人排隊到您的'OperationExecutor'並使用'Task.Run'代替。 –
不幸的是,改變實現以便'OperationExecuter'取而代之並使用'Task.Run'產生相同的結果。 –