I ran into the following problem: I start a task to process responses from a bot in a telegram for a long execution
Task.Factory.StartNew(() => _.RunAsync(), TaskCreationOptions.LongRunning)
_ - is an instance of my service
method code below:
/// <inheritdoc />
public async Task RunAsync()
{
// запуск метода рассылки пользователям сообщений со стоимостями их портфелей
await _notifier.RunAsync(_cancellationTokenSource.Token);
var updateReceiver = _telegramClient.GetUpdateReceiver(
new[]
{
UpdateType.Message,
UpdateType.CallbackQuery,
});
try
{
Logger.Info("Revaluate portfolios service successfully launched!");
await foreach (var update in updateReceiver.WithCancellation(_cancellationTokenSource.Token))
{
var updateModel = new UpdateModel()
{
Text = update.Message?.Text ?? update.CallbackQuery?.Data,
ChatId = update.Message?.Chat.Id ?? update.CallbackQuery?.Message?.Chat.Id ?? 0,
Phone = update.Message?.Contact?.PhoneNumber ?? "",
From = update.Message?.From ?? update.CallbackQuery?.From
};
await HandleMessageAsync(updateModel);
}
}
catch (OperationCanceledException exception)
{
Logger.Error(exception, "The service was stopped by token cancellation. Reason: ");
throw;
}
catch (Exception ex)
{
Logger.Error(ex);
throw;
}
finally
{
Dispose();
}
}
I assume that the error is called from a method await HandleMessageAsync(updateModel);
where I used _cancellationTokenSource.Token.
I received next error on my server (Ubuntu 2G RAM, 2 Core, 40G SSD):
RevaluatePortfoliosService|The service was stopped by token cancellation. Reason: System.OperationCanceledException: The operation was canceled
But nothing could cause the cancellation. I don't understand why this is happening
Related
I would like to know if the code I produced is good practice and does not produce leaks, I have more than 7000 objects Participant which I will push individually and Handle the Response to save the "external" id in our database. First I use the Parallel ForEach on the list pPartcipant:
Parallel.ForEach(pParticipant, participant =>
{
try
{
//Create
if (participant.id == null)
{
ExecuteRequestCreate(res, participant);
}
else
{//Update
ExecuteRequestUpdate(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
});
Then I do a classic (not async request), but after I need to "handle" the response (print in the console, save in a text file in async mode, and Update in my database)
private async void ExecuteRequestCreate(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
//lTaskAll.Add(Task.Run(() => { ExecuteAll(request, pRes, pParticipant); }));
//Task.Run(() => ExecuteAll(request, pRes, pParticipant));
var result = RestExecute(request, pRes);
await HandleResult(result, pParticipant);
//lTaskHandle.Add(Task.Run(() => { HandleResult(result, pParticipant); }));
}
catch (Exception e)
{
lTaskLog.Add(LogHelper.Log(e.Message + " " + e.InnerException));
}
}
Should I run a new task for handeling the result (as commented) ? Will it improve the performance ?
In comment you can see that I created a list of tasks so I can wait all at the end (tasklog is all my task to write in a textfile) :
int nbtask = lTaskHandle.Count;
try
{
Task.WhenAll(lTaskHandle).Wait();
Task.WhenAll(lTaskLog).Wait();
}
catch (Exception ex)
{
LogHelper.Log("Fail on API calls tasks", ex);
}
I don't have any interface it is a console program.
I would like to know if the code I produced is good practice
No; you should avoid async void and also avoid Parallel for async work.
Here's a similar top-level method that uses asynchronous concurrency instead of Parallel:
var tasks = pParticipant
.Select(participant =>
{
try
{
//Create
if (participant.id == null)
{
await ExecuteRequestCreateAsync(res, participant);
}
else
{//Update
await ExecuteRequestUpdateAsync(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
})
.ToList();
await Task.WhenAll(tasks);
And your work methods should be async Task instead of async void:
private async Task ExecuteRequestCreateAsync(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
var result = await RestExecuteAsync(request, pRes);
await HandleResult(result, pParticipant);
}
catch (Exception e)
{
LogHelper.Log(e.Message + " " + e.InnerException);
}
}
I have started to explore TPL in .NET to implement a windows service which will run multiple independent methods in parallel with some delay after each successful execution, following is a rough console application I came up with after looking at various examples:
class Program
{
static void Main(string[] args)
{
CancellationTokenSource _ct1 = new CancellationTokenSource();
CancellationTokenSource _ct2 = new CancellationTokenSource();
int count = 0;
var task1 = new Task(async () =>
{
try
{
while (true)
{
DoWork();
await Task.Delay(2000, _ct1.Token);
count++;
if (count >= 5)
{
throw new NotImplementedException();
}
}
}
catch (TaskCanceledException ex)
{
//Log cancellation and do not continue execution
Console.WriteLine("DoWork cancelled: " + ex.Message);
}
catch (Exception ex)
{
//Log error and continue with execution
Console.WriteLine("Error occurred at DoWork");
}
}, _ct1.Token, TaskCreationOptions.LongRunning);
var task2 = new Task(async () =>
{
try
{
while (true)
{
DoWork2();
await Task.Delay(2000, _ct2.Token);
count++;
if (count >= 5)
{
_ct2.Cancel();
}
}
}
catch (TaskCanceledException ex)
{
//Log cancellation and do not continue execution
Console.WriteLine("DoWork2 cancelled: " + ex.Message);
}
catch (Exception ex)
{
//Log error and continue with execution
Console.WriteLine("Error occurred at DoWork");
}
}, _ct2.Token, TaskCreationOptions.LongRunning);
task1.Start();
task2.Start();
Console.ReadKey();
}
public static void DoWork()
{
Console.WriteLine("Doing something...");
}
public static void DoWork2()
{
Console.WriteLine("Doing something else...");
}
}
In the above code when any exception occurs during the task execution, I need to log the error and then continue with the task execution, right now the task stops executing if there is an exception. My questions:
How to handle exceptions properly so that the task execution doesn't stop?
When I add a debugger break point at DoWork() method, the DoWork2() doesn't run which mean the tasks are not running in parallel on separate threads and running on a single thread and blocking each other. How to make sure the Tasks are running independent of each other on separate threads?
PS: The above code is a simple console app just to understand the workings of TPL, so please ignore if there are obvious design problems.
I believe that the simplest chage you can do to make your task1 running after exception is as follows:
var task1 = new Task(async () =>
{
while (true)
{
try
{
DoWork();
await Task.Delay(2000, _ct1.Token);
count++;
if (count >= 5)
{
throw new NotImplementedException();
}
}
catch (TaskCanceledException ex)
{
//Log cancellation and do not continue execution
Console.WriteLine("DoWork cancelled: " + ex.Message);
break;
}
catch (Exception ex)
{
//Log error and continue with execution
Console.WriteLine("Error occurred at DoWork");
count = 0; // without this you'll have exception thrown on each of further iterations
}
}
}, _ct1.Token, TaskCreationOptions.LongRunning);
Meaning that try...catch is moved inside the loop.
Please also note #pull420 comment on while you don't see task2 running
I've got a Post method in my webapi 2 controller that does an insert into a database but often has to retry for many seconds before it succeeds. Basically, that causes a lot of sleeps between the retries. Effectively, it is running the code below. My question is, is this code correct so that I can have thousands of these running at the same time and not using up my iis page pool?
public async Task<HttpResponseMessage> Post()
{
try
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
Thread.Sleep(30000);
//InsertInTable(json);
}
catch (Exception ex)
{
throw ex;
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
* Added By Peter As To Try Stephens's suggestion of Await.Delay. Shows error that can not put await in catch.
public async Task<HttpResponseMessage> PostXXX()
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
bool success = false;
int retriesMax = 30;
int retries = retriesMax;
while (retries > 0)
{
try
{
// DO ADO.NET EXECUTE THAT MAY FAIL AND NEED RETRY
retries = 0;
}
catch (SqlException exception)
{
// exception is a deadlock
if (exception.Number == 1205)
{
await Task.Delay(1000);
retries--;
}
// exception is not a deadlock
else
{
throw;
}
}
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
* More Added By Peter, Trying Enterprise block, missing class (StorageTransientErrorDetectionStrategy class not found)
public async Task<HttpResponseMessage> Post()
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2));
var retryPolicy =
new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy);
try
{
// Do some work that may result in a transient fault.
retryPolicy.ExecuteAction(
() =>
{
// Call a method that uses Windows Azure storage and which may
// throw a transient exception.
Thread.Sleep(10000);
});
}
catch (Exception)
{
// All the retries failed.
}
*****Code that causes SqlServer to spin out of control with open connections
try
{
await retryPolicy.ExecuteAsync(
async () =>
{
// this opens a SqlServer Connection and Transaction.
// if fails, rolls back and rethrows exception so
// if deadlock, this retry loop will handle correctly
// (caused sqlserver to spin out of control with open
// connections so replacing with simple call and
// letting sendgrid retry non 200 returns)
await InsertBatchWithTransaction(sendGridRecordList);
});
}
catch (Exception)
{
Utils.Log4NetSimple("SendGridController:POST Retries all failed");
}
and the async insert code (with some ...'s)
private static async Task
InsertBatchWithTransaction(List<SendGridRecord> sendGridRecordList)
{
using (
var sqlConnection =
new SqlConnection(...))
{
await sqlConnection.OpenAsync();
const string sqlInsert =
#"INSERT INTO SendGridEvent...
using (SqlTransaction transaction =
sqlConnection.BeginTransaction("SampleTransaction"))
{
using (var sqlCommand = new SqlCommand(sqlInsert, sqlConnection))
{
sqlCommand.Parameters.Add("EventName", SqlDbType.VarChar);
sqlCommand.Transaction = transaction;
try
{
foreach (var sendGridRecord in sendGridRecordList)
{
sqlCommand.Parameters["EventName"].Value =
GetWithMaxLen(sendGridRecord.EventName, 60);
await sqlCommand.ExecuteNonQueryAsync();
}
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
}
}
}
No. At the very least, you want to replace Thread.Sleep with await Task.Delay. Thread.Sleep will block a thread pool thread in that request context, doing nothing. Using await allows that thread to return to the thread pool to be used for other requests.
You might also want to consider the Transient Fault Handling Application Block.
Update: You can't use await in a catch block; this is a limitation of the C# language in VS2013 (the next version will likely allow this, as I note on my blog). So for now, you have to do something like this:
private async Task RetryAsync(Func<Task> action, int retries = 30)
{
while (retries > 0)
{
try
{
await action();
return;
}
catch (SqlException exception)
{
// exception is not a deadlock
if (exception.Number != 1205)
throw;
}
await Task.Delay(1000);
retries--;
}
throw new Exception("Retry count exceeded");
}
To use the Transient Fault Handling Application Block, first you define what errors are "transient" (should be retried). According to your example code, you only want to retry when there's a SQL deadlock exception:
private sealed class DatabaseDeadlockTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
{
public bool IsTransient(Exception ex)
{
var sqlException = ex as SqlException;
if (sqlException == null)
return false;
return sqlException.Number == 1205;
}
public static readonly DatabaseDeadlockTransientErrorDetectionStrategy Instance = new DatabaseDeadlockTransientErrorDetectionStrategy();
}
Then you can use it as such:
private static async Task RetryAsync()
{
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2));
var retryPolicy = new RetryPolicy(DatabaseDeadlockTransientErrorDetectionStrategy.Instance, retryStrategy);
try
{
// Do some work that may result in a transient fault.
await retryPolicy.ExecuteAsync(
async () =>
{
// TODO: replace with async ADO.NET calls.
await Task.Delay(1000);
});
}
catch (Exception)
{
// All the retries failed.
}
}
I'm having a hard time tracing an exception within a Task. The message returned from the exception isn't helpful at all. Below is the method that appears to be throwing the exception:
public async Task<bool> TestProxy(IPEndPoint proxy)
{
Ping ping = new Ping();
PingReply reply = await ping.SendPingAsync(proxy.Address);
if (reply == null || reply.Status != IPStatus.Success) return false;
HttpClient client = new HttpClient();
HttpResponseMessage response;
try
{
response = await client.GetAsync("https://google.com");
} catch(Exception ex)
{
return false;
Console.WriteLine(ex.Message);
}
if(response.IsSuccessStatusCode)
{
return true;
}
else
{
return false;
}
And here is the calling code:
public async Task<List<IPEndPoint>> FetchWorkingProxiesAsync()
{
List<IPEndPoint> _proxies = await FetchRawProxiesAsync();
List<IPEndPoint> workingProxies = new List<IPEndPoint>();
Task[] tasks = new Task[_proxies.Count];
for(int i = 0; i < _proxies.Count; i++)
{
IPEndPoint _tmpProxy = _proxies[i];
tasks[i] = Task.Run(async () => {
try
{
if (await TestProxy(_tmpProxy))
{
workingProxies.Add(_tmpProxy);
}
}catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
});
}
await Task.WhenAll(tasks);
return workingProxies;
}
The exception that is being thrown is:
InnerException {"The request was aborted: The request was canceled."} System.Exception {System.Net.WebException}
When I set breakpoints no helpful information is available on the exception. I see that there's a System.Net.WebException but it doesn't say what or why.
I have CLR Throw and User Exceptions ticked in the Exceptions dialogue in VS.
Edit: I just realised I am not using the proxy to test the connection when attempting to connect to google.com. Even so, I'd still like to know how to find out more about the exception thrown.
The fact that we can't use the await keyword in catch blocks makes it quite awkward to show error messages from async methods in WinRT, since the MessageDialog API is asynchronous. Ideally I would like be able to write this:
private async Task DoSomethingAsync()
{
try
{
// Some code that can throw an exception
...
}
catch (Exception ex)
{
var dialog = new MessageDialog("Something went wrong!");
await dialog.ShowAsync();
}
}
But instead I have to write it like this:
private async Task DoSomethingAsync()
{
bool error = false;
try
{
// Some code that can throw an exception
...
}
catch (Exception ex)
{
error = true;
}
if (error)
{
var dialog = new MessageDialog("Something went wrong!");
await dialog.ShowAsync();
}
}
All methods that need to do this have to follow a similar pattern, which I really don't like, because it reduces the code readability.
Is there a better way to handle this?
EDIT: I came up with this (which is similar to what svick suggested in his comments):
static class Async
{
public static async Task Try(Func<Task> asyncAction)
{
await asyncAction();
}
public static async Task Catch<TException>(this Task task, Func<TException, Task> handleExceptionAsync, bool rethrow = false)
where TException : Exception
{
TException exception = null;
try
{
await task;
}
catch (TException ex)
{
exception = ex;
}
if (exception != null)
{
await handleExceptionAsync(exception);
if (rethrow)
ExceptionDispatchInfo.Capture(exception).Throw();
}
}
}
Usage:
private async Task DoSomethingAsync()
{
await Async.Try(async () =>
{
// Some code that can throw an exception
...
})
.Catch<Exception>(async ex =>
{
var dialog = new MessageDialog("Something went wrong!");
await dialog.ShowAsync();
});
}
.Catch<...> calls can be chained to mimick multiple catch blocks.
But I'm not really happy with this solution; the syntax is even more awkward than before...
you already have that functionality in TPL
await Task.Run(async () =>
{
// Some code that can throw an exception
...
}).ContinueWith(async (a) =>
{
if (a.IsFaulted)
{
var dialog = new MessageDialog("Something went wrong!\nError: "
+ a.Exception.Message);
await dialog.ShowAsync();
}
else
{
var dialog2 = new MessageDialog("Everything is OK: " + a.Result);
await dialog2.ShowAsync();
}
}).Unwrap();
In this machine I don't have Windows 8 so I tested in Windows 7 but I think is the same.
*Edit
as stated in the comments its needed .Unwrap(); in the end for the await to work
C# 6 now supports await in catch and finally, so the code can be written the way I wanted it; a workaround is no longer needed.