I need to know how to stop task in pararell foreach loop when exception occurs? If Foo1() thrown an exception DoSomeWork() should be cancelled.
Item class
class Item
{
public async Task DoSomeWork()
{
try
{
await Foo1();
await Foo2();
}
catch (Exception ex)
{
Console.Writeline(ex.Message);
}
}
private async Task Foo1()
{
try
{
//
// some functionality
//
}
catch(Exception ex)
{
// If exception occurs, then how cancel Task DoSomeWork()
}
}
private async Task Foo2()
{
try
{
//
// some functionality
//
}
catch(Exception ex)
{
// If exception occurs, then how cancel Task DoSomeWork()
}
}
}
and the ItemCollection class with Pararell.Foreach loop
class ItemCollection
{
public void StartAsync()
{
try
{
IList<Task> il = new List<Task>();
Parallel.ForEach(ListOfItems, t => il.Add(t.DoSomeWork()));
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
How to use the CancellationToken property in this example?
Here is a good example from MSDN: https://msdn.microsoft.com/de-de/library/ee256691(v=vs.110).aspx
Before the loops starts:
CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
Inside the loop:
Parallel.ForEach(nums, po, (num) =>
{
// Do some work here
po.CancellationToken.ThrowIfCancellationRequested();
});
From another thread, you can trigger the cancel event:
cts.Cancel();
Related
I am trying to get a Windows Service running. The service should use a worker object to spawn multiple tasks.
I use SemaphoreSlim in both the worker object and each task to wait for events to finish, like so:
public static IHostBuilder ConfigureServices(this IHostBuilder builder)
{
builder.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<WorkerService>();
services.AddSingleton<WorkerClient>();
});
return builder;
}
WorkerService
public WorkerService(ILogger<WorkerService> logger, WorkerClient workerClient)
{
_logger = logger;
_workerClient = workerClient;
_bleClient.OnValuesReceived += _bleClient_OnValuesReceived;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await _workerClient.Run();
}
catch(Exception ex)
{
_logger.LogCritical(ex, "Error while running worker client.");
}
await Task.Delay(TimeSpan.FromSeconds(_scanDelay), stoppingToken);
}
}
WorkerClient
public class WorkerClient
{
private Scanner _scanner;
private SemaphoreSlim _lock;
public WorkerClient()
{
_lock = new SemaphoreSlim(0, 1);
_scanner = new Scanner();
_scanner.OnScanFinished += scanner_ScanFinished;
}
public async Task Run()
{
_scanner.Scan();
await _lock.WaitAsync();
}
private void scanner_ScanFinished(object sender, string[] macs)
{
var tasks = new List<Task>();
foreach(var mac in macs)
{
var client = new TaskRunner(mac);
tasks.Add(client.Run());
}
if(tasks.Count > 0)
{
try
{
var task = Task.WhenAll(tasks.ToArray());
await task;
}
catch(Exception ex)
{
_logger.LogError(ex, ex.Message);
}
}
_lock.Release();
}
}
TaskRunner
public class TaskRunner
{
private SemaphoreSlim _lock;
private Client _client;
public TaskRunner(string mac)
{
_lock = new SemaphoreSlim(0, 1);
_client = new Client(mac);
_client.OnWorkFinished += client_WorkFinished;
}
public async Task Run()
{
_client.DoWork();
await _lock.WaitAsync();
}
private void client_WorkFinished(object sender, EventArgs args)
{
_lock.Release();
}
}
This whole construct runs fine when I launch it in a console or inside VS. But it hangs after 1-2 runs when I create a service using sc utility and start it.
I have no idea what I am doing wrong as I am very new to Windows Services and multithreading.
The SemaphoreSlim is probably not an adequate mechanism for converting an event to a Task, because it can't propagate exceptions. The TaskCompletionSource class is a more suitable mechanism for this purpose. Also when subscribing to an event it is a good idea to unsubscribe when we don't want to receive any further notifications. Unsubscribing is achieved using the -= operator.
Here are two extension methods for the classes Scanner and Client, that allow to subscribe to their specific events for a single notification, and propagate this notification as a Task.
public static class ScannerExtensions
{
public static Task<string[]> ScanAsync(this Scanner source)
{
var tcs = new TaskCompletionSource<string[]>();
Action<object, string[]> evenHandler = null;
evenHandler = (s, macs) =>
{
source.OnScanFinished -= evenHandler;
tcs.TrySetResult(macs);
};
source.OnScanFinished += evenHandler;
try
{
source.Scan();
}
catch (Exception ex)
{
source.OnScanFinished -= evenHandler;
tcs.SetException(ex);
}
return tcs.Task;
}
}
public static class ClientExtensions
{
public static Task DoWorkAsync(this Client source)
{
var tcs = new TaskCompletionSource<object>();
EventHandler evenHandler = null;
evenHandler = (s, e) =>
{
source.OnWorkFinished -= evenHandler;
tcs.TrySetResult(null);
};
source.OnWorkFinished += evenHandler;
try
{
source.DoWork();
}
catch (Exception ex)
{
source.OnWorkFinished -= evenHandler;
tcs.SetException(ex);
}
return tcs.Task;
}
}
You could use the extension methods Scanner.ScanAsync and Client.DoWorkAsync to refactor the ExecuteAsync method of your service like this:
private Scanner _scanner = new Scanner();
protected override async Task ExecuteAsync(CancellationToken token)
{
while (true)
{
Task delayTask = Task.Delay(TimeSpan.FromSeconds(_scanDelay), token);
try
{
string[] macs = await _scanner.ScanAsync();
Task[] doWorktasks = macs.Select(mac =>
{
var client = new Client(mac);
return client.DoWorkAsync();
}).ToArray();
await Task.WhenAll(doWorktasks);
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
}
await delayTask;
}
}
Not sure if this will solve your problem, but I think that it is a change in the right direction.
If the problem persists you could try creating and awaiting the client.DoWorkAsync tasks one at a time (instead of launching all of them concurrently), to see if that makes any difference.
How can I implement a timeout for a potentially long running action lambda? I have tried wrapping the action in a Task and then using a cancellation token to enforce the timeout but to no avail.
public void LongRunningTask()
{
ExecuteSafely(() => //do something );
}
private void ExecuteSafely(Action action)
{
try
{
action();
}
catch
{
// just don't crash
}
}
New implementation as per 1st answer:
public void Information<T>(string messageTemplate, T propertyValue)
{
ExecuteSafely(() => _logger.Information(messageTemplate, propertyValue));
}
private void ExecuteSafely(Action action)
{
try
{
var cts = new CancellationTokenSource();
cts.CancelAfter(new TimeSpan(0, 0, 1));
try
{
Task.Run(action, cts.Token).Wait();
}
catch (TaskCanceledException e)
{
System.Diagnostics.Debug.WriteLine(e.ToString());
//here you know the task is cancelled due to timeout
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.ToString());
// just don't crash
}
}
This is only working correctly when I step into Task.Run but not when I step over or let it run. Perhaps there is some closure issue with the lambda being passed into the action?
You should use CancellationTokenSource
void Execute(Action action, TimeSpan timeout)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);
try
{
Task.Run(action, cts.Token).Wait();
}
catch (TaskCanceledException e)
{
//here you know the task is cancelled due to timeout
}
}
For some reason I could not catch an exception thrown inside anonymous async delegate that subscribed to event.
It does not get caught inside TestTestAsync (I suppose because of invoke wait only fastest one) but why it is not caught in unhandled or unobserved or crash app?
ThrowUnobservedTaskExceptions = true also does not make any sense.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp5
{
class Program
{
static string lockStr = Guid.NewGuid().ToString();
public static void ConsoleWriteLine(string Message, ConsoleColor? color = null)
{
lock (lockStr)
{
var old = Console.ForegroundColor;
if (color != null)
Console.ForegroundColor = color.Value;
Console.WriteLine(Message);
if (color != null)
Console.ForegroundColor = old;
}
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
try
{
var cls = new TestClass();
cls.TestAsync += async (s) => await Cls_TestRealAsyncAsync(s);
cls.TestAsync += Cls_TestRealAsync;
Task.Run(async () => await cls.TestTestAsync()).Wait();
Thread.Sleep(5000);
}
catch (Exception ex)
{
ConsoleWriteLine($"{nameof(Main)}: {ex.Message}");
}
}
private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
ConsoleWriteLine($"{nameof(TaskScheduler_UnobservedTaskException)}: {(e.Exception as Exception).Message}", ConsoleColor.Yellow);
}
private static Task Cls_TestRealAsync(object sender)
{
try
{
Thread.Sleep(100);
throw new NotImplementedException($"{nameof(Cls_TestRealAsync)}");
}
catch (Exception ex)
{
ConsoleWriteLine(ex.Message, ConsoleColor.Red);
throw;
}
}
private static async Task Cls_TestRealAsyncAsync(object sender)
{
try
{
await Task.Run(() => Thread.Sleep(1000));
throw new NotImplementedException($"{nameof(Cls_TestRealAsyncAsync)}");
}
catch (Exception ex)
{
ConsoleWriteLine(ex.Message, ConsoleColor.Red);
throw;
}
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
ConsoleWriteLine($"{nameof(CurrentDomain_UnhandledException)}: {(e.ExceptionObject as Exception).Message}", ConsoleColor.Yellow);
}
}
public class TestClass
{
public delegate Task TestHandlerAsync(object sender);
public event TestHandlerAsync TestAsync;
private async Task OnTestAsync()
{
if (TestAsync != null)
await TestAsync.Invoke(this);
}
public async Task TestTestAsync()
{
try
{
await OnTestAsync();
}
catch (Exception ex)
{
Program.ConsoleWriteLine($"{nameof(TestTestAsync)}: {ex.Message}", ConsoleColor.Green);
}
}
}
}
PS: I made tests on 4.7.1
Asynchronous code is not necessarily concurrent code, but you should be careful anyway.
This:
private async Task OnTestAsync()
{
if (TestAsync != null)
await TestAsync.Invoke(this);
}
can get you in trouble because by the time TestAsync.Invoke is invoked, TestAsync can be null.
But the problem that you're trying to solve is that, not the that the fastest one is awaited but that the last one is awaited.
You should revise your API but, if you can't, try this:
public class TestClass
{
public delegate Task TestHandlerAsync(object sender);
public event TestHandlerAsync TestAsync;
private async Task OnTestAsync()
{
var testAsync = this.TestAsync;
if (testAsync == null)
{
return;
}
await Task.WhenAll(
from TestHandlerAsync d in testAsync.GetInvocationList()
select d.Invoke(this));
}
public async Task TestTestAsync()
{
try
{
await OnTestAsync();
}
catch (Exception ex)
{
Program.ConsoleWriteLine($"{nameof(TestTestAsync)}: {ex.Message}", ConsoleColor.Green);
}
}
}
if you only care to show the first exception.
Or:
public class TestClass
{
public delegate Task TestHandlerAsync(object sender);
public event TestHandlerAsync TestAsync;
private async Task<Exception[]> OnTestAsync()
{
var testAsync = this.TestAsync;
if (testAsync == null)
{
return new Exception[0];
}
return await Task.WhenAll(
from TestHandlerAsync d in testAsync.GetInvocationList()
select ExecuteAsync(d));
async Task<Exception> ExecuteAsync(TestHandlerAsync d)
{
try
{
await d(this);
return null;
}
catch (Exception ex)
{
return ex;
}
}
}
public async Task TestTestAsync()
{
try
{
var exceptions = await OnTestAsync();
foreach (var exception in exceptions)
{
if (exception != null)
{
Program.ConsoleWriteLine($"{nameof(TestTestAsync)}: {exception.Message}", ConsoleColor.Green);
}
}
}
catch (Exception ex)
{
Program.ConsoleWriteLine($"{nameof(TestTestAsync)}: {ex.Message}", ConsoleColor.Green);
}
}
}
if you care for all.
Found the answer. It not abandoned. It simply still not fired because of life of my test console was too short.
Unhandled exception will be thrown at GC.Collect()
https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/
During GC, it notices that nobody ever checked the result (and
therefore never saw the exception) and so bubbles it up as an
unobserved exception.
So next code before main method end will solve issue and I see exception
GC.Collect();
Thread.Sleep(5000);
I am using an asynchronous method. How can I stop its execution when a Timer raises a timeout event?
My code:
public async Task<object> Method()
{
cts = new CancellationTokenSource();
try
{
timer = new System.Timers.Timer(3000);
timer.Start();
timer.Elapsed += (sender, e) =>
{
try
{
timer_Elapsed(sender, e, cts.Token, thread);
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
return;
}
};
await methodAsync(cts.Token);
return "message";
}
catch (OperationCanceledException)
{
return "cancelled";
}
catch (Exception ex)
{
return ex.Message;
}
}
// Async Call
public async Task<object> methodAsync(CancellationToken ct)
{
try
{
pdfDocument = htmlConverter.Convert("path", "");
}
catch(Exception ex)
{
return x.Message;
}
}
// Timer event
void timer_Elapsed(object sender, ElapsedEventArgs e, CancellationToken ct)
{
cts.Cancel();
ct.ThrowIfCancellationRequested();
}
Here's how canceling a task works:
public async Task<object> Method()
{
cts = new CancellationTokenSource();
await methodAsync(cts.Token);
return "message";
}
public Task<object> methodAsync(CancellationToken ct)
{
for (var i = 0; i < 1000000; i++)
{
if (ct.IsCancellationRequested)
{
break;
}
//Do a small part of the overall task based on `i`
}
return result;
}
You have to respond to the change in the property of ct.IsCancellationRequested to know when to cancel the task. There is no safe way for one thread/task to cancel another thread/task.
In your case it appears that you are trying to call a single method that doesn't know about the CancellationToken so you can not cancel this task safely. You must let the thread/task continue to completion.
I think you can try mentioning when to cancel it. Something like
cts.CancelAfter(TimeSpan.FromMilliseconds(5000));
Also, you need to use the cancellation token in the called methods. That's when you will know when to cancel.
How can i handle an exception on wpf, that was throwed within worker thread?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
var task = Task.Factory.StartNew(() =>
{
Debug.WriteLine("Hello");
throw new Exception();
});
try
{
task.Wait();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
Or it is not common to handle an exception in this way?
task.ContinueWith(task => {
if (task .Exception != null)
{
//.........
}
},TaskContinuationOptions.OnlyOnFaulted);
Take a look here http://www.codeproject.com/Articles/152765/Task-Parallel-Library-of-n#handlingExceptions
You can catch the [System.AggregateException] to examine if you can handle any of its InnerExceptions. See the example below.
var task = Task.Factory.StartNew(() =>
{
Debug.WriteLine("Hello");
throw new InvalidOperationException(); // throw an InvalidOperationException that is handled.
});
try
{
task.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
if (x is InvalidOperationException) // We know how to handle this exception.
{
Console.WriteLine("InvalidOperationException error.");
return true; // Continue with operation.
}
return false; // Let anything else stop the application.
});
}