I have two async methods that I am running in the background of a form window as separate threads/tasks. These are infinite loops that just do some work in the background and then update the UI using the dispatcher. See below.
public async Task RunCameraThread(CancellationToken cancelToken) { while (true) { // If cancellation token is set, get out of the thread & throw a cancel exception cancelToken.ThrowIfCancellationRequested(); // Get an image from the camera CameraBitmap = Camera.CaptureImage(true); // Update the UI (use lock to prevent simultaneous use of Dispatcher object in other thread) lock (Dispatcher) { Dispatcher.Invoke(() => pictureBoxCamera.Image = tempBitmap); Dispatcher.Invoke(() => pictureBoxCamera.Invalidate()); } } } public async Task RunDistanceSensorThread(CancellationToken cancelToken) { while (true) { // If cancellation token is set, get out of the thread & throw a cancel exception cancelToken.ThrowIfCancellationRequested(); // Get the distance value from the distance sensor float distance = Arduino.AverageDistance(10, 100); // Update the UI (use lock to prevent simultaneous use of Dispatcher object) lock (Dispatcher) { Dispatcher.Invoke(() => textBoxDistanceSensor.Text = distance.ToString("0.00")); } } } These tasks are started on a button click (code shown below). I'm trying to use await Task.WhenAll in order to await both tasks. When the cancellation token is set this works as intended and an OperationCanceledException is caught. However, any exceptions thrown by issues with the Camera or Arduino (simulated by simply unplugging the USB during a run), does not seem to be caught.
private async void buttonConnect_Click(object sender, EventArgs e) { try { // Disable UI so we cannot click other buttons DisableComponentsUI(); // Connect to Nimbus, Camera and Arduino await Task.Run(() => Nimbus.ConnectAsync()); Camera.Connect(); Camera.ManagedCam.StartCapture(); Arduino.Connect(); // Get the current Nimbus positions and enable UI UpdatePositionsUI(); EnableComponentsUI(); // Reset cancel token and start the background threads and await on them (this allows exceptions to bubble up to this try/catch statement) StopTokenSource = new CancellationTokenSource(); var task1 = Task.Run(() => RunCameraThread(StopTokenSource.Token)); var task2 = Task.Run(() => RunDistanceSensorThread(StopTokenSource.Token)); await Task.WhenAll(task1, task2); } catch (OperationCanceledException exceptionMsg) { // Nothing needed here... } catch (Hamilton.Components.TransportLayer.ObjectInterfaceCommunication.ComLinkException exceptionMsg) { NimbusExceptionHandler(exceptionMsg); } catch (FlyCapture2Managed.FC2Exception exceptionMsg) { CameraExceptionHandler(exceptionMsg); } catch (IOException exceptionMsg) { ArduinoExceptionHandler(exceptionMsg); } catch (UnauthorizedAccessException exceptionMsg) { ArduinoExceptionHandler(exceptionMsg); } catch (TimeoutException exceptionMsg) { ArduinoExceptionHandler(exceptionMsg); } } What's strange is that I see the exceptions thrown in the output window, but they don't bubble up to my try/catch. Also, if I simply await on one task it works as expected and the exception bubbles up.
Anyone have any idea what I'm doing wrong?
Thanks!
WhenAllwill throw anAggregateExceptionthat contains the exceptions form every single exception from every task that faulted, not just the first.await Task.WhenAllin the last example here, however a caveat that may be important for this question is: "the task might be the result of a call to Task.WhenAll. When you await such a task, only one of the exceptions is caught, and you can't predict which exception will be caught.".