http://msdn.microsoft.com/en-us/library/dd997415.aspx
Per the article referenced above I am trying to handle exceptions in a continuatin task. The example I am citing in the above article is this:
var task1 = Task.Factory.StartNew(() =>
{
throw new MyCustomException("Task1 faulted.");
})
.ContinueWith((t) =>
{
Console.WriteLine("I have observed a {0}",
t.Exception.InnerException.GetType().Name);
},
TaskContinuationOptions.OnlyOnFaulted);
My code is:
Task<string> task = Task<string>.Factory.StartNew(() => process.StartTask(this));
task.ContinueWith(CloseDialog, TaskContinuationOptions.OnlyOnFaulted);
In StartTask, I throw an error just like the example. My expectation is that CloseDialog will execute and I can examine task.Exception within that method as shown by the example. However when I throw the exception I the code simply stops with an unhandled exception. Should I be using a try/catch block? If so, where?
By the way, I want my continuation task (CloseDialog) to ALWAYS run. I'm just using .OnlyOnFaulted because that is what is shown in the example.
A continuation can find out if an exception was thrown by the antecedent Task by the antecedent task's exception property. The following prints the results of a NullReferenceException to the console
Task task1 = Task.Factory.StartNew (() => { throw null; });
Task task2 = task1.ContinueWith (ant => Console.Write(ant.Exception());
If task1 throws an exception and this exception is not captured/queried by the continuation it is considered unhandled and the application dies. With continuations it is enough to establish the result of the task via the Status keyword
asyncTask.ContinueWith(task =>
{
// Check task status.
switch (task.Status)
{
// Handle any exceptions to prevent UnobservedTaskException.
case TaskStatus.RanToCompletion:
if (asyncTask.Result)
{
// Do stuff...
}
break;
case TaskStatus.Faulted:
if (task.Exception != null)
mainForm.progressRightLabelText = task.Exception.InnerException.Message;
else
mainForm.progressRightLabelText = "Operation failed!";
default:
break;
}
}
If you don't use continuations you either have to wait on the task in a try/catch block or query a task's Result in a try/catch block
int x = 0;
Task<int> task = Task.Factory.StartNew (() => 7 / x);
try
{
task.Wait();
// OR.
int result = task.Result;
}
catch (AggregateException aggEx)
{
Console.WriteLine(aggEx.InnerException.Message);
}
Hope this help even if it is a bit late and you know all there is by now! :]
Related
I'm trying to find the best way to run a Task from a dedicated background thread.
The context of usage is consuming from a Kafka topic and raising an async event handler to handle the ConsumeResult<TKey, TValue> instance.
A Kafka Consumer (the consumer instance below) blocks the thread until a message is consumed or the CancellationToken it is passed has been cancelled.
consumeThread = new Thread(Consume)
{
Name = "Kafka Consumer Thread",
IsBackground = true,
};
This is the implementation of the Consume method I came up with, which is started by the dedicated thread above:
private void Consume(object _)
{
try
{
while (!cancellationTokenSource.IsCancellationRequested)
{
var consumeResult = consumer.Consume(cancellationTokenSource.Token);
var consumeResultEventArgs = new ConsumeResultReceivedEventArgs<TKey, TValue>(
consumer, consumeResult, cancellationTokenSource.Token);
_ = Task.Run(async () =>
{
if (onConsumeResultReceived is null) continue;
var handlerInstances = onConsumeResultReceived.GetInvocationList();
foreach (ConsumeResultReceivedEventHandler<TKey, TValue> handlerInstance in handlerInstances)
{
if (cancellationTokenSource.IsCancellationRequested) return;
await handlerInstance(this, consumeResultEventArgs).ConfigureAwait(false);
}
}, cancellationTokenSource.Token);
}
}
catch (OperationCanceledException)
{
}
catch (ThreadInterruptedException)
{
}
catch (ThreadAbortException)
{
// Aborting a thread is not implemented in .NET Core.
}
}
I'm not sure this is the recommened way to run a Task from a dedicated Thread, so any advice would be very much appreciated.
It's not clear to me why you need a dedicated thread at all. The code as it currently stands starts a thread and then that thread blocks for consumption and then raises the event handler on a thread pool thread.
The _ = Task.Run idiom is a "fire and forget", which is dangerous in the sense that it will silently swallow any exceptions from your event raising code or event handlers.
I'd recommend replacing Thread with Task.Run, and just raising the event handlers directly:
consumeTask = Task.Run(ConsumeAsync);
private async Task ConsumeAsync()
{
while (true)
{
var consumeResult = consumer.Consume(cancellationTokenSource.Token);
var consumeResultEventArgs = new ConsumeResultReceivedEventArgs<TKey, TValue>(
consumer, consumeResult, cancellationTokenSource.Token);
if (onConsumeResultReceived is null) continue;
var handlerInstances = onConsumeResultReceived.GetInvocationList();
foreach (ConsumeResultReceivedEventHandler<TKey, TValue> handlerInstance in handlerInstances)
{
if (cancellationTokenSource.IsCancellationRequested) return;
await handlerInstance(this, consumeResultEventArgs).ConfigureAwait(false);
}
}
}
This thread I have written has three try catches. The first is a try with resources to set up an ObjectOutputStream. The second recieves information from another on whether authentication has succeeded or failed. On success it should establish a map utilised in communication and on failure the thread is returned from. Likewise if an Interrupt or IOException occurs in this phase, the thread is returned from. Only in the eventuality of successful authentication as far as I can see should the second try catch be reached. This second block is responsible for handling packets it receives util the session ends either through interrupt or a packet requesting it. My problem is that on ending a session I am required to replace the aforementioned ConcurrenHashMap record pertaining to this thread with an empty Optional. this should occur in both previously outlined shutdown mechanisms. However the line responsible for this in the InterruptedException catch block says the map may not have been initialised despite the fact that it should be impossible to reach that block without its initialisation.
public void run(){
boolean quit = false;
Packet packet;
int cid, nid;
ConcurrentMap<Integer, Optional<BlockingQueue<Packet>>> channelMap;
try (ObjectOutputStream output = new ObjectOutputStream(new
BufferedOutputStream(sslSocket.getOutputStream()))) {
try {
packet = channel.take();
if (packet.getType() == AUTH_SUCCESS) {
cid = ((AuthSuccessPacket) packet).getCid();
nid = ((AuthSuccessPacket) packet).getNid();
channelMap = networkMap.get(nid);
channelMap.replace(cid, Optional.of(channel));
output.writeObject(packet);
} else {
output.writeObject(packet);
return;
}
}catch (IOException | InterruptedException e){
return;
}
while (!quit && !interrupted()) {
try {
packet = channel.take();
switch (packet.getType()) {
case ACK:
case MESSAGE:
case REQUEST_USER:
case RELAY_SHUTDOWN:
output.writeObject(packet);
break;
case END_SESSION:
if (packet.getSource() == cid) {
output.writeObject(packet);
channelMap.replace(cid, Optional.empty());
quit = true;
}
break;
}
}catch (IOException e){}
}
}catch (InterruptedException e){
channelMap.replace(cid, Optional.empty());
} catch (IOException e){}
}
What am I missing? Thanks.
I am not sure what the cause was but I moved the Interrupt to a separate catch of the second inner block and it no longer raised the exception.
I have implemented the Azure - Offline Sync based on the documentation / Sample provided by Microsoft Sample in my Xamarin Forms Application.
In the sample / documentation provided, they are using the default Service Handler.
// Simple error/conflict handling. A real application would handle the various errors like network conditions,server conflicts and others via the IMobileServiceSyncHandler.
Since I need to implement a retry logic for 3 times if the Pull / Push fails. As per the documentation I have created a custom Service Handler(IMobileServiceSyncHandler).
Please find my code logic here.
public class CustomSyncHandler : IMobileServiceSyncHandler
{
public async Task<JObject> ExecuteTableOperationAsync(IMobileServiceTableOperation operation)
{
MobileServiceInvalidOperationException error = null;
Func<Task<JObject>> tryExecuteAsync = operation.ExecuteAsync;
int retryCount = 3;
for (int i = 0; i < retryCount; i++)
{
try
{
error = null;
var result = await tryExecuteAsync();
return result;
}
catch (MobileServiceConflictException e)
{
error = e;
}
catch (MobileServicePreconditionFailedException e)
{
error = e;
}
catch (MobileServiceInvalidOperationException e)
{
error = e;
}
catch (Exception e)
{
throw e;
}
if (error != null)
{
if(retryCount <=3) continue;
else
{
//Need to implement
//Update failed, reverting to server's copy.
}
}
}
return null;
}
public Task OnPushCompleteAsync(MobileServicePushCompletionResult result)
{
return Task.FromResult(0);
}
}
But I am not sure how to handle / revert server copy in case all the 3 retry failed.
In the TODO sample they where reverting it based on the
MobileServicePushFailedException. But which is available when we implement IMobileServiceSyncHandler.
More over if we include custom IMobileServiceSyncHandler it wont execute the code after PushAsync / PullAsync. Even the try catch wont fire in case any exception.
try
{
await this.client.SyncContext.PushAsync();
await this.todoTable.PullAsync(
//The first parameter is a query name that is used internally by the client SDK to implement incremental sync.
//Use a different query name for each unique query in your program
"allTodoItems",
this.todoTable.CreateQuery());
}
catch (MobileServicePushFailedException exc)
{
if (exc.PushResult != null)
{
syncErrors = exc.PushResult.Errors;
}
}
// Simple error/conflict handling. A real application would handle the various errors like network conditions,
// server conflicts and others via the IMobileServiceSyncHandler.
if (syncErrors != null)
{
foreach (var error in syncErrors)
{
if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
{
//Update failed, reverting to server's copy.
await error.CancelAndUpdateItemAsync(error.Result);
}
else
{
// Discard local change.
await error.CancelAndDiscardItemAsync();
}
Debug.WriteLine(#"Error executing sync operation. Item: {0} ({1}). Operation discarded.", error.TableName, error.Item["id"]);
}
}
}
Note
In my application I am only trying to achieve retry for 3 time in case any server error. I am not looking for to resolve conflicts. Thant is the reason I haven't added the code for the same.
If someone came across similar issues and resolved it please help.
Stez.
You say you aren't trying to resolve conflicts, but you need to resolve them one way or another (without telling the user what's going on, perhaps) by accepting the server version of the object or updating the client operation. Otherwise it will just keep telling you about the same conflict each time it retries the operation.
You need to have a subclass of the Microsoft.WindowsAzure.MobileServices.Sync.MobileServiceSyncHandler class, which overrides OnPushCompleteAsync() in order to handle conflicts and other errors. Let's call the class SyncHandler:
public class SyncHandler : MobileServiceSyncHandler
{
public override async Task OnPushCompleteAsync(MobileServicePushCompletionResult result)
{
foreach (var error in result.Errors)
{
await ResolveConflictAsync(error);
}
await base.OnPushCompleteAsync(result);
}
private static async Task ResolveConflictAsync(MobileServiceTableOperationError error)
{
Debug.WriteLine($"Resolve Conflict for Item: {error.Item} vs serverItem: {error.Result}");
var serverItem = error.Result;
var localItem = error.Item;
if (Equals(serverItem, localItem))
{
// Items are the same, so ignore the conflict
await error.CancelAndUpdateItemAsync(serverItem);
}
else // check server item and local item or the error for criteria you care about
{
// Cancels the table operation and discards the local instance of the item.
await error.CancelAndDiscardItemAsync();
}
}
}
Include an instance of this SyncHandler() when you initialize your MobileServiceClient:
await MobileServiceClient.SyncContext.InitializeAsync(store, new SyncHandler()).ConfigureAwait(false);
Read up on the MobileServiceTableOperationError to see other conflicts you can handle as well as its methods to allow resolving them.
The exception carries with it a copy of the server version. In my implementation of IMobileServiceSyncHandler I therefore just return error.Value and this seems to work.
A more extensive example of this kind of logic can be found in this MSDN blog.
The same author has another example where he shows how you can resolve the conflict in favour of the server copy or the client copy, here.
(In case you're interested, the background for this question is here, but I don't think it's critical for this particular question.)
We're trying to run a series of report exports (a third party method call) one at a time in separate threads, so we can kill off the thread if it takes too long. The ugly, but best-so-far, idea is to use Thread.Abort to kill the thread exporting a given report, then do a ResetAbort to allow the rest of the code to continue.
The proof of concept code looks like this:
public RunningMethod()
{
Report myReport = new Report();
for (int i = 0; i < 10; i++)
{
Thread reportThread = new Thread(() => DoBackgroundJob(myReport, "test" + i.ToString()));
reportThread.Start();
bool finished = reportThread.Join(TimeSpan.FromMilliseconds(100));
if (!finished)
{
reportThread.Abort();
}
}
}
protected void DoBackgroundJob(Report myReport, string reportFilename)
{
try
{
report.ExportToPdf(#"C:\" + reportFilename + ".pdf");
}
catch (ThreadAbortException)
{
}
break;
}
I'm getting a strange result when I run this...the Export line seems to throw an exception that seems like it should be a ThreadAbortException, but apparently is not, since it doesn't get caught by the catch (ThreadAbortException), but is caught by a catch (Exception).
I'd like to know what kind of exception I'm getting, but I can't see it because when I try to view it I only get "Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack."
Is there a way to determine what is happening? What exception is really being thrown here?
I am using following code to call a web service in update UI using Task
//------- REFRESH BOOK LIST ------
public Task<string> GetBookList()
{
return Task.Factory.StartNew(() => {
// GET BOOK LIST
WebServiceController webServices = new WebServiceController ();
string bookList = webServices.GetBookList ();
if (bookList.Contains("BooksList")) {
// PARSE
ParseListData parseData = new ParseListData ();
parseData.ParseList (bookList);
}
return bookList;
});
}
I call this code using
GetBookList ().ContinueWith (task => {
if (task.IsFaulted) {
// STOP ACTIVITY INDICATOR
RemoveActivityIndicator (true);
throw new AggregateException (task.Exception.InnerException.Message);
}
// RUNS WHEN TASK IS FINISHED
InvokeOnMainThread (() => {
// STOP ACTIVITY INDICATOR
RemoveActivityIndicator (true);
string bookList = task.Result;
if (bookList.Contains("Error:") || !bookList.Contains("BooksList"))
{
// SHOW ERROR MESSAGE
}
});
});
If there is an error in the return string (bookList) then i want to check for bookList.Contains("Error:") as above and show error message. The problem in that is bookList string is assigned in the Task GetBookList() function. How do i get that value in GetBookList ().ContinueWith to show error.
How to write a Task in above situation to return a string.
Use Task<string>, then the Task's Result property can be used to store your String value.