If I make a CancellationTokenSource and pass it down to a place where I perform a query like this:
await command.Connection.OpenAsync();
dataReader = await command.ExecuteReaderAsync(_cancellationToken);
If immediately below this I add the following:
_cancellationToken.ThrowIfCancellationRequested();
resultTable.Load(dataReader);
If on a different thread _cancellationTokenSource.Cancel() is called, will the query be cancelled appropriately?
Was able to find a decent solution to the problem. Given the cancellation token and a SQLCommand, wrapping the loading to a data table as follows successfully cancels the db query:
using (CancellationTokenRegistration ctr = cancellationToken.Register(() => cmd.Cancel()))
{
using (var reader = cmd.ExecuteReaderAsync(cancellationToken))
{
dataTable.Load(reader.Result);
}
}
So when the cancellationToken is cancelled in a different thread, the cmd.Cancel is called automatically. This will throw a SqlException that I had to handle and perform my cleanup.
Related
The original method is the inefficient foreach loop that awaits every job (which is an I/O bound network call):
foreach (var job in Jobs)
{
try
{
await DoJobAsync(job); //Pass job to external vendor API
job.Succeeded = true;
}
catch (Exception)
{
//do nothing, let loop continue
}
}
Now to improve performance, I want to use Task.WhenAll to process all jobs in a non-blocking manner.
However, we need to make sure that each job object only has that Succeeded property set to true if the DoJobAsync task does not throw exception.
If we do this:
await Task.WhenAll(Jobs.Select(j =>
{
var task = DoJobAsync(j);
j.Succeeded = true;
return task;
}));
My understanding is that if any job task ends up throwing exception, that property will still get toggled to true because each individual task is not awaited as it is created, making the code flow go right past it.
I know I can capture the Task returned by Task.WhenAll to have access to a list of all exceptions thrown, but I can't figure out a way to use them to trace back to the job that threw the exception.
How do I work around this issue?
You could use an async delegate as the selector of the Select operator:
await Task.WhenAll(Jobs.Select(async job =>
{
await DoJobAsync(job);
job.Succeeded = true;
}));
This way each job will be projected to a Task that will not be the original DoJobAsync(job) task, but instead a wrapper task that encapsulates the logic for updating the Succeeded property. This property will be updated immediately after the DoJobAsync(job) task completes successfully.
It is possible that multiple Job objects might have their Succeeded property updated in parallel. It depends on whether a SynchronizationContext is installed on the current thread.
Return the result true/false from the method DoJobAsync().
This way you can return the result directly.
await Task.WhenAll(Jobs.Select(j =>
{
return DoJobAsync(j);
}));
I am trying to insert a record in azure table on some specific condition. What I found that the records are not getting saved all the time. Few times it works and few times its not. Here is the code which I have written to execute the insert. In addition to that, the code is written to insert a single record for each call and its never going to save multiple records at a time. Also I don't want to wait for the insert operation nor I want to do it synchronously. I want to do it asynchronously and that is without wait.
why the code sometimes fails to insert the records?
Here is my code snippet
try
{
AuditUtils utils = new AuditUtils((int)this.User.Id);
String tableName = "ServiceHeaderInfo", partitionKey = this.Header.PrimaryAccount;
TableAuditEntity tableAuditEntity = utils.GetAuditEntity(tableName, partitionKey, Guid.NewGuid().ToString(), TableAuditTypes.Insert, refId, refType);
tableAuditEntity["DeviceIdentifier"] = this.Header.DeviceId;
tableAuditEntity["DeviceModel"] = this.Header.DeviceModel;
tableAuditEntity["OSVersion"] = this.Header.OSVersion;
utils.AddAsync(tableAuditEntity).ContinueWith(t =>
{
var ex = t.Exception?.GetBaseException();
if (ex != null)
{
this.HandleError(ex);
}
}, System.Threading.Tasks.TaskContinuationOptions.OnlyOnFaulted);
}
catch (Exception ex)
{
this.HandleError(ex);
}
public async Task<TableResult> AddAsync(string tableName, SampleTableEntity entity, CancellationToken cancellationToken = default(CancellationToken))
{
var table = await GetTableAsync(tableName);
return await table.ExecuteAsync(TableOperation.InsertOrReplace(entity), cancellationToken);
}
It is not possible Async without Wait.
You need to use
ExecuteAsync(TableOperation)`
or
ExecuteAsync(TableOperation, TableRequestOptions, OperationContext) methods is used to achieve the requirement.
Instead of above methods you have
ExecuteAsync(TableOperation, CancellationToken)
&
ExecuteAsync(TableOperation, TableRequestOptions, OperationContext, CancellationToken)
When you use CancellationToken it must have a TaskFactory.ContinueWhenAll method.
In a cancellation of the operation, it instantiates a CancellationTokenSource object that generates a cancellation token that's passed to a TaskFactory object.
In turn, the TaskFactory object passes the cancellation token to each of the tasks responsible for collecting readings for a particular instrument.
The TaskFactory.ContinueWhenAll<TAntecedentResult, TResult>(Task<TAntecedentResult>[], Func<Task<TAntecedentResult>[],TResult>, CancellationToken) method is called to ensure that computed only after all readings have been gathered successfully.
If a task has not completed because it was cancelled, the TaskFactory.ContinueWhenAll method throws an exception.
Please refer: click here
I need your help, with threads I'm full 0 and you only need to create a certain thread and complete it on command, BUT I do not create each thread in advance, as there will be a lot of them, I do it like this:
Thread thread = new Thread(() => Go(..... many many variables that are taken from the listview ......));
thread.Start();
So, as noted above, variables are taken from the listview, which in turn is loaded by me from the file and then I run the threads I need. BUT the process in the stream is infinite and will end only if I completely close the program, and I would like to end the stream in the same way as I started it (right click on the desired line-start/stop). As I said, I have never worked with threads and thought that it was somehow simple, like when you start a thread, you assign it an ID and end it with the same ID, but alas. I have searched all over Google and have not found an EXAMPLE that suits me (I will repeat for the third time - I have never worked with threads and I do not need to say "go read about TPL"), so I ask for help, preferably with an example)
I have a very bad idea: in the sheet there is an invisible column in which an id is generated at the start, then when I send a command to start the thread, a unique variable is created with the name for example int id1=0 and its name is passed to the thread itself and each time the loop starts, id1=0 or 1 is checked in it, respectively, if 0-continue, if 1-empty. Well, it is logical that when you click the stop button, its value changes to 1. But something seems to me that the holy spirit of multithreading will punish me for this when the threads become 100+. I read this idea somewhere, so don't swear)
You do not need hundreds of threads for this. Your worker "threads" are performing HTTP requests, which can be done asynchronously without requiring a new thread. Also, hundreds of threads wouldn't really help you unless you have hundreds of CPU cores (you don't).
For this sort of work, I'd recommend the following:
Write a method that does all the work your thread does, but also checks a CancellationToken with each iteration.
Calls the method in a loop, once for each account, and store the resulting tasks in an array or list. Or use LINQ (as I do in this example) to create the list.
When your program terminates, activate the CancellationToken.
After cancelling, you have to await all the tasks in order to observe any possible exceptions and exit cleanly.
For example
public async Task DoTheWork(Account account, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var result = await httpClient.GetAsync(account.Url);
await DoSomethingWithResult(result);
await Task.Delay(1000);
}
}
//Main program
var accounts = GetAccountList();
var source = new CancellationTokenSource();
var tasks = accounts.Select( x => DoTheWork(x, source.Token) ).ToList();
//When exiting
source.Cancel();
await Task.WhenAll( tasks );
source.Dispose();
Indivivdual cancellation
Here's another approach that keeps a list of the accounts and a delegate that can be used for cancelling the task for that specific account.
//Declare this somewhere it will persist for the duration of the program
//The key to this dictionary is the account you wish to cancel
//The value is a delegate that you can call to cancel its task
Dictionary<Account, Func<Task>> _tasks = new Dictionary<Account, Func<Task>>();
async Task CreateTasks()
{
var accounts = GetAccounts();
foreach (var account in accounts)
{
var source = new CancellationTokenSource();
var task = DoTheWork(account, source.Token);
_tasks.Add(account, () => { source.Cancel(); return task; });
}
}
//Retrieve the delegate from the dictionary and call it to cancel its task
//Then await the task to observe any exceptions
//Then remove it from the list
async Task CancelTask(Account account)
{
var cancelAction = _tasks[account];
var task = cancelAction();
await task;
_tasks.Remove(account);
}
async Task CancelAllTasks()
{
var tasks = _tasks.Select(x => x.Value()).ToList();
await Task.WhenAll(tasks);
}
I'm trying to cancel a cosmos query using the cancellation token using the NuGet package Microsoft.Azure.Cosmos (3.13.0).
Can anyone help explain why it takes so long to cancel?
This test shows that it takes 2000+ Milliseconds to cancel. I was expecting it to fail milliseconds after I cancelled it.
00158: Reading next
00160: Read next
00188: Cancelling
02492: The operation was canceled.
public class CosmosCancelationTests
{
private readonly ITestOutputHelper testOutputHelper;
public CosmosCancelationTests(ITestOutputHelper testOutputHelper)
{
this.testOutputHelper = testOutputHelper;
}
[Fact]
public async Task TestCancelationTime()
{
Stopwatch stopwatch = Stopwatch.StartNew();
try
{
var client = new CosmosClient(
"https://localhost:8081/",
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
);
var database = client.GetDatabase("CosmosDBPackage"); // Make sure this database is created in the cosmos instance
var container = database.GetContainer("SampleEntity"); // Make sure this container is created in the database
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
using var feedIterator = container.GetItemLinqQueryable<SampleEntity>()
.Where(x => false)
.ToFeedIterator();
if (feedIterator.HasMoreResults)
{
testOutputHelper.WriteLine($"{stopwatch.ElapsedMilliseconds:D5}: Reading next");
var task = feedIterator.ReadNextAsync(cancellationToken);
testOutputHelper.WriteLine($"{stopwatch.ElapsedMilliseconds:D5}: Read next");
await Task.Delay(20);
cancellationTokenSource.Cancel();
testOutputHelper.WriteLine($"{stopwatch.ElapsedMilliseconds:D5}: Cancelling");
await task;
}
}
catch (CosmosOperationCanceledException e)
{
testOutputHelper.WriteLine($"{stopwatch.ElapsedMilliseconds:D5}: {e.Message}");
}
}
}
CancellationTokens in .NET is a mechanism that by definition, won't cause any library to stop what it's doing immediately, it is a cooperative cancellation. The library will cancel the operation when it's safe and won't cause a corrupt or invalid state. For example, HttpClient, when you call SendAsync and the token cancels, if the client already started buffering the response, it won't cancel, it will wait until it completes.
In the case of the query, it will check the token, when it is safe and won't cause an invalid state, maybe the cancellation is happening while requests are on the wire, or responses are being aggregated.
Reference: https://learn.microsoft.com/en-us/dotnet/standard/threading/cancellation-in-managed-threads
I have the function that I call asynchronously using the await keyword:
public Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName)
{
var tcs = new TaskCompletionSource<StateInfo>();
try
{
var propInstance = BuildCacheKey(entity, propName);
StateCacheItem cacheItem;
if (_stateCache.TryGetValue(propInstance, out cacheItem))
{
tcs.SetResult( new StateInfo (cacheItem.State.Name, cacheItem.State.Value) );
return tcs.Task;
}
//state not found in local cache so save the tcs for later and request the state
var cacheKey = BuildCacheKey(entity, propName);
_stateRequestItemList.TryAdd(cacheKey, new StateRequestItem(entity, propName, tcs));
_evtClient.SubmitStateRequest(entity, propName);
return tcs.Task;
}
catch (Exception ex)
{
tcs.SetException(ex);
return tcs.Task;
}
}
The function has a look to see if it has the information it needs and if it does it returns it. If it doesn’t have the details it sends a request out which should eventually come in as an event. At that point my code (not shown) finds the stored TaskCompletionSource item, sets the result and returns it. This all works fine but I have now been asked to consider a situation where a reply may never be returned when I request state via the “_evtClient.SubmitStateRequest(entity, propName);” line. I need to implement some sort of timeout mechanism so I can cancel the TCS task so the function caller can fail gracefully. I’ve been looking on SO and the internet and can’t find anything that looks right. I’m now not sure if I need to restructure the above code in a different way. Can anyone advise or point me to a similar scenario?
The code that calls the above function can call it in a single hit like this:
var stateProperty = await RequestStateForEntity(key, stateName);
or in a batch, like this:
await
Task.WhenAll(
stateDefinitions.Select(stateDefinition => stateDefinition.Name)
.Select(
stateName =>
Task.Factory.StartNew(
async () => results.Add(await RequestStateForEntity(key, stateName)))
.Unwrap())
.ToArray());
First off, what you really want to enable is cancellation. The fact that the cancellation comes from a timeout is just a footnote.
.NET has some great built-in support for cancellation, and the Task-based Asynchronous Pattern prescribes how to use it.
Essentially, what you want to do is take a CancellationToken:
Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName,
CancellationToken cancellationToken);
Next, you want to respond when that token is signaled. Ideally, you would want to just pass the CancellationToken down to the _evtClient so that the request is truly cancelled:
_evtClient.SubmitStateRequest(entity, propName, cancellationToken);
This is the normal way of implementing cancellation, and it works great if SubmitStateRequest already understands cancellation. Often the event arguments have a flag indicating cancellation (e.g., AsyncCompletedEventArgs.Cancelled). If at all possible, use this approach (i.e., change _evtClient as necessary to support cancellation).
But sometimes this just isn't possible. In this case, you can choose to pretend to support cancellation. What you're actually doing is just ignoring the request if it completes after it was cancelled. This is not the most ideal situation but sometimes you have no choice.
Personally, I don't really like this kind of approach since it makes the API "lie": the method signature claims to support cancellation but it really is just faking it. So first, I recommend documenting this. Put in a code comment apology explaining that _evtClient doesn't support cancellation, and the "cancellation" is actually just pretend cancellation.
Then, you'll need to hook into the CancellationToken yourself, after the state request item is in the list but before the actual request is sent:
var item = new StateRequestItem(entity, propName, tcs);
_stateRequestItemList.TryAdd(cacheKey, item);
item.CancellationRegistration = cancellationToken.Register(() =>
{
StateRequestItem cancelledItem;
if (!_stateRequestItemList.TryRemove(cacheKey, out cancelledItem))
return;
cancelledItem.TaskCompletionSource.TrySetCanceled();
});
_evtClient.SubmitStateRequest(entity, propName);
Finally, you'll need to update your event handler completion code (not shown) to ignore the situation where the state request item has already been removed, and to dispose the CancellationRegistration if the state request item is found.
Once your method supports cancellation, then it's easy to cancel via a timer:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
CancellationToken token = cts.Token;
or from any other kind of situation. Say, if the user cancels whatever (s)he's doing. Or if another part of the system decides it doesn't need that data anymore. Once your code supports cancellation, it can handle cancellation for any reason.
You may pass a CancellationToken to your method, which can internally implement the cancellation logic:
public Task<StatePropertyEx> RequestStateForEntity(
EntityKey entity, string propName, CancellationToken token)
{
var tcs = new TaskCompletionSource<StateInfo>();
try
{
// Cache checking
_evtClient.SubmitStateRequest(entity, propName, token);
return tcs.Task;
}
catch (Exception ex)
{
tcs.SetException(ex);
return tcs.Task;
}
}
And inside SubmitStateRequest:
token.ThrowIfCancellationRequest();
Note ThrowIfCancellationRequest will throw a OperationCanceledException which you will need to handle. If you are making a blocking call, you can internally set the CancellationTokenSource with a TimeSpan:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));