If I start the following example in .NET Core BackgroundService on debug mode:
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Task.Run(async () => await Task.Delay(30000, stoppingToken))
.Wait(stoppingToken);
}
}
the Ctrl + C cancellation event does not call the StopAsync() Method which is calling the Cancel() from the CancellationTokenSource.
I think my problem is similar to this post.
How can I catch those cancellations when I'm using blocking methods inside the ExecuteAsync?
p.s.: In the real world my ExecuteAsync is watching the filesystem until a new file is created in my destination. To achieve this behavior I'm using the FileSystemWatcher.WaitForChanged() method.
From the comments, it looks like the problem has little to do with threads. The real problem is how to stop a FileSystemWatcher.
You don't need an extra thread with a FileSystemWatcher, you need to handle its change events as quickly as possible. You can use an asynchronous event handler for this, or even better, quickly post events to a queue or Channel for processing.
To stop the FSW you can use the CancellationToken.Register method to set EnableRaisingEvents to false :
stoppingToken.Register(()=>watcher.EnableRaisingEvents=false);
Event processing
To quickly handle events, one could post the FileSystemEventArgs values directly to a queue or a Channel and process them with another tasks. This has two benefits:
File events are handled as fast as possible, so none is lost
The code can either wait for all events to finish, or cancel them.
var channel=Channel.CreateUnbounded<FileSystemEventArgs>();
stoppingToken.Register(()=>{
watcher.EnableRaisingEvents=false;
channel.Writer.TryComplete();
});
watcher.Changed+=(o,e)=>channel.Writer.WriteAsync(e,stoppingToken);
await foreach(var e in channel.Reader.ReadAllAsync(stoppingToken))
{
//do something
}
A Channel can be treated as a queue with asynchronous read and write operation. The ReadAllAsync method dequeues messages until stopped and returns them as an IAsyncEnumerable which allows the use of await foreach to easily handle items asynchronously.
Pipelines and Channels
The code can be refactored into this:
await watcher.AsChannel(stoppingToken)
.ProcessEvents(stoppingToken);
The consumer
It's easy to extract the subscriber code into a separate method. This could even be an extension method:
public static async Task ProcessEvents(this ChannelReader<FileSystemEventArgs> reader,CancellationToken stoppingToken)
{
await foreach(var e in channel.Reader.ReadAllAsync(stoppingToken))
{
//do something
}
}
And call it :
var channel=Channel.CreateUnbounded<FileSystemEventArgs>();
stoppingToken.Register(()=>{
watcher.EnableRaisingEvents=false;
channel.Writer.TryComplete();
});
watcher.Changed+=(o,e)=>channel.Writer.WriteAsync(e,stoppingToken);
await ProcessEvents(channel,stoppingToken);
This works because a Channel has implicit cast operators to ChannelReader and ChannelWriter.
A ChannelReader supports multiple consumers, so one could use multiple tasks to process events, eg :
public static async Task ProcessEvents(this ChannelReader<FileSystemEventArgs> reader,int dop,CancellationToken stoppingToken)
{
var tasks=Enumerable.Range(0,dop).Select(()=>{
await foreach(var e in channel.Reader.ReadAllAsync(stoppingToken))
{
//do something
}
});
await Task.WhenAll(tasks);
}
The producer
It's also possible to extract the channel creation and posting into a separate method. After all, we only need the ChannelReader for processing:
public static ChannelReader AsChannel(this FileSystemWatcher watcher, CancellationToken stoppingToken)
{
var channel=Channel.CreateUnbounded<FileSystemEventArgs>();
stoppingToken.Register(()=>{
watcher.EnableRaisingEvents=false;
channel.Writer.TryComplete();
});
watcher.Changed+=(o,e)=>channel.Writer.WriteAsync(e,stoppingToken);
return channel;
}
And combine everything in a simple pipeline:
await watcher.AsChannel(stoppingToken)
.ProcessEvents(stoppingToken);
My first workaround at the moment is this:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var blockingTask = Task.Run(async () => await Task.Delay(30000, stoppingToken));
await Task.WhenAny(blockingTask);
}
}
#Panagiotis Kanavos I appreciate your efforts, I'm coming back to your detailed post If I'm trying to change my "blocking" FSW to an event driven FSW.
In productive I'm using something like this:
private void DoServiceWork()
{
// Some Work if new PDF or docx file is available
// ...
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
int myTimeout = 1000 * 60 * 60; // 1 hour
while (!stoppingToken.IsCancellationRequested)
{
pdfWatchingTask = Task.Run(() => MyFSWLibrary.Watch(directory, "*.pdf", myTimeout, stoppingToken));
docWatchingTask = Task.Run(() => MyFSWLibrary.Watch(directory, "*.docx", myTimeout, stoppingToken));
var finishedTask = await Task.WhenAny(new Task<MyFSWResult>[] { waitPdfTask, waitXmpTask });
if(finishedTask.Result.Success) DoServiceWork();
}
}
Related
I have an ASP.Net Core Web API application which consumes messages from an AMQ Queue. Currently I have the consuming code in a BackgroundService with an event handler hooked up to the Listener. The whole thing is in a while look (checking the cancellation token) to ensure any errors are handled and we retry the subscription but I also have an inner while loop to keep the service alive but it doesn't need to do anything.
My question is, what should I do inside that inner while loop to make sure I don't consume unnecessary CPU; e.g. Task.Yield(), Task.Delay(something)?
public class ReceiverService : BackgroundService
{
...
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
...
while (!stoppingToken.IsCancellationRequested)
{
...
IConnectionFactory factory =
new NMSConnectionFactory(
$"activemq:ssl://{parsed?["message"]}:51513?wireFormat.maxInactivityDuration=0");
connection = await factory.CreateConnectionAsync(Username, Password);
var session = await connection.CreateSessionAsync();
var destination = await session.GetQueueAsync("queuename/" + subscriptionId);
var consumer = await session.CreateConsumerAsync(destination);
consumer.Listener += async message =>
{
// do stuff with message
message.Acknowledge();
};
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(0, stoppingToken);
}
await connection?.CloseAsync()!;
await Task.Delay(1000, stoppingToken);
}
}
}
Cheers
Rich
If you have nothing but cleanup to do, then you can just do await Task.Delay(Timeout.InfiniteTimeSpan, stoppingToken);. No need for any loops at all.
Command receiver:
public override async ValueTask Execute(){
// my code here
// this is boardcast command
// attempt #1
Parallel.ForEach(lists, async list => { await DoSomething(); });
// attempt #2
List<Task> tasks = new List<Task>();
foreach(ulong id in ids) tasks.Add(DoSomething());
await Task.WhenAll(tasks);
}
DoSomething():
public async Task DoSomething(){
Task<abc> Request = API.RequestSomething();
// attempt #1
await Request.ContinueWith(Callback => {
// Do something (long time...) , eg: update database, update all client data etc.
});
// attempt #2
await Request.ContinueWith(Callback => {
// .....
}, TaskContinuationOptions.ExecuteSynchronously);
}
Question is : If client sent command to receiver rapidly (about 100 commands per seconds), how can I handle this without (or low) delay, or how can I make my task function (Task DoSomeThing();) run in parallel
I have tested my code and it works fine. But I would like your advice if there is a better way. Or is there any mistake that I'm missing? (I haven't tested it with multiple commands per second).
As others have noted, Parallel should not be used with async methods, so the Task.WhenAll approach is the correct solution for asynchronous concurrency:
public override async ValueTask Execute() {
var tasks = ids.Select(id => DoSomething()).ToList();
await Task.WhenAll(tasks);
}
Regarding your implementation, do not use ContinueWith:
public async Task DoSomething() {
var Callback = await API.RequestSomething();
// Do something (long time...) , eg: update database, update all client data etc.
}
I have few methods that report some data to Data base. We want to invoke all calls to Data service asynchronously. These calls to data service are all over and so we want to make sure that these DS calls are executed one after another in order at any given time. Initially, i was using async await on each of these methods and each of the calls were executed asynchronously but we found out if they are out of sequence then there are room for errors.
So, i thought we should queue all these asynchronous tasks and send them in a separate thread but i want to know what options we have? I came across 'SemaphoreSlim' . Will this be appropriate in my use case?
Or what other options will suit my use case? Please, guide me.
So, what i have in my code currently
public static SemaphoreSlim mutex = new SemaphoreSlim(1);
//first DS call
public async Task SendModuleDataToDSAsync(Module parameters)
{
var tasks1 = new List<Task>();
var tasks2 = new List<Task>();
//await mutex.WaitAsync(); **//is this correct way to use SemaphoreSlim ?**
foreach (var setting in Module.param)
{
Task job1 = SaveModule(setting);
tasks1.Add(job1);
Task job2= SaveModule(GetAdvancedData(setting));
tasks2.Add(job2);
}
await Task.WhenAll(tasks1);
await Task.WhenAll(tasks2);
//mutex.Release(); // **is this correct?**
}
private async Task SaveModule(Module setting)
{
await Task.Run(() =>
{
// Invokes Calls to DS
...
});
}
//somewhere down the main thread, invoking second call to DS
//Second DS Call
private async Task SendInstrumentSettingsToDS(<param1>, <param2>)
{
//await mutex.WaitAsync();// **is this correct?**
await Task.Run(() =>
{
//TrackInstrumentInfoToDS
//mutex.Release();// **is this correct?**
});
if(param2)
{
await Task.Run(() =>
{
//TrackParam2InstrumentInfoToDS
});
}
}
Initially, i was using async await on each of these methods and each of the calls were executed asynchronously but we found out if they are out of sequence then there are room for errors.
So, i thought we should queue all these asynchronous tasks and send them in a separate thread but i want to know what options we have? I came across 'SemaphoreSlim' .
SemaphoreSlim does restrict asynchronous code to running one at a time, and is a valid form of mutual exclusion. However, since "out of sequence" calls can cause errors, then SemaphoreSlim is not an appropriate solution since it does not guarantee FIFO.
In a more general sense, no synchronization primitive guarantees FIFO because that can cause problems due to side effects like lock convoys. On the other hand, it is natural for data structures to be strictly FIFO.
So, you'll need to use your own FIFO queue, rather than having an implicit execution queue. Channels is a nice, performant, async-compatible queue, but since you're on an older version of C#/.NET, BlockingCollection<T> would work:
public sealed class ExecutionQueue
{
private readonly BlockingCollection<Func<Task>> _queue = new BlockingCollection<Func<Task>>();
public ExecutionQueue() => Completion = Task.Run(() => ProcessQueueAsync());
public Task Completion { get; }
public void Complete() => _queue.CompleteAdding();
private async Task ProcessQueueAsync()
{
foreach (var value in _queue.GetConsumingEnumerable())
await value();
}
}
The only tricky part with this setup is how to queue work. From the perspective of the code queueing the work, they want to know when the lambda is executed, not when the lambda is queued. From the perspective of the queue method (which I'm calling Run), the method needs to complete its returned task only after the lambda is executed. So, you can write the queue method something like this:
public Task Run(Func<Task> lambda)
{
var tcs = new TaskCompletionSource<object>();
_queue.Add(async () =>
{
// Execute the lambda and propagate the results to the Task returned from Run
try
{
await lambda();
tcs.TrySetResult(null);
}
catch (OperationCanceledException ex)
{
tcs.TrySetCanceled(ex.CancellationToken);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
});
return tcs.Task;
}
This queueing method isn't as perfect as it could be. If a task completes with more than one exception (this is normal for parallel code), only the first one is retained (this is normal for async code). There's also an edge case around OperationCanceledException handling. But this code is good enough for most cases.
Now you can use it like this:
public static ExecutionQueue _queue = new ExecutionQueue();
public async Task SendModuleDataToDSAsync(Module parameters)
{
var tasks1 = new List<Task>();
var tasks2 = new List<Task>();
foreach (var setting in Module.param)
{
Task job1 = _queue.Run(() => SaveModule(setting));
tasks1.Add(job1);
Task job2 = _queue.Run(() => SaveModule(GetAdvancedData(setting)));
tasks2.Add(job2);
}
await Task.WhenAll(tasks1);
await Task.WhenAll(tasks2);
}
Here's a compact solution that has the least amount of moving parts but still guarantees FIFO ordering (unlike some of the suggested SemaphoreSlim solutions). There are two overloads for Enqueue so you can enqueue tasks with and without return values.
using System;
using System.Threading;
using System.Threading.Tasks;
public class TaskQueue
{
private Task _previousTask = Task.CompletedTask;
public Task Enqueue(Func<Task> asyncAction)
{
return Enqueue(async () => {
await asyncAction().ConfigureAwait(false);
return true;
});
}
public async Task<T> Enqueue<T>(Func<Task<T>> asyncFunction)
{
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
// get predecessor and wait until it's done. Also atomically swap in our own completion task.
await Interlocked.Exchange(ref _previousTask, tcs.Task).ConfigureAwait(false);
try
{
return await asyncFunction().ConfigureAwait(false);
}
finally
{
tcs.SetResult();
}
}
}
Please keep in mind that your first solution queueing all tasks to lists doesn't ensure that the tasks are executed one after another. They're all running in parallel because they're not awaited until the next tasks is startet.
So yes you've to use a SemapohoreSlim to use async locking and await. A simple implementation might be:
private readonly SemaphoreSlim _syncRoot = new SemaphoreSlim(1);
public async Task SendModuleDataToDSAsync(Module parameters)
{
await this._syncRoot.WaitAsync();
try
{
foreach (var setting in Module.param)
{
await SaveModule(setting);
await SaveModule(GetAdvancedData(setting));
}
}
finally
{
this._syncRoot.Release();
}
}
If you can use Nito.AsyncEx the code can be simplified to:
public async Task SendModuleDataToDSAsync(Module parameters)
{
using var lockHandle = await this._syncRoot.LockAsync();
foreach (var setting in Module.param)
{
await SaveModule(setting);
await SaveModule(GetAdvancedData(setting));
}
}
One option is to queue operations that will create tasks instead of queuing already running tasks as the code in the question does.
PseudoCode without locking:
Queue<Func<Task>> tasksQueue = new Queue<Func<Task>>();
async Task RunAllTasks()
{
while (tasksQueue.Count > 0)
{
var taskCreator = tasksQueue.Dequeu(); // get creator
var task = taskCreator(); // staring one task at a time here
await task; // wait till task completes
}
}
// note that declaring createSaveModuleTask does not
// start SaveModule task - it will only happen after this func is invoked
// inside RunAllTasks
Func<Task> createSaveModuleTask = () => SaveModule(setting);
tasksQueue.Add(createSaveModuleTask);
tasksQueue.Add(() => SaveModule(GetAdvancedData(setting)));
// no DB operations started at this point
// this will start tasks from the queue one by one.
await RunAllTasks();
Using ConcurrentQueue would be likely be right thing in actual code. You also would need to know total number of expected operations to stop when all are started and awaited one after another.
Building on your comment under Alexeis answer, your approch with the SemaphoreSlim is correct.
Assumeing that the methods SendInstrumentSettingsToDS and SendModuleDataToDSAsync are members of the same class. You simplay need a instance variable for a SemaphoreSlim and then at the start of each methode that needs synchornization call await lock.WaitAsync() and call lock.Release() in the finally block.
public async Task SendModuleDataToDSAsync(Module parameters)
{
await lock.WaitAsync();
try
{
...
}
finally
{
lock.Release();
}
}
private async Task SendInstrumentSettingsToDS(<param1>, <param2>)
{
await lock.WaitAsync();
try
{
...
}
finally
{
lock.Release();
}
}
and it is importend that the call to lock.Release() is in the finally-block, so that if an exception is thrown somewhere in the code of the try-block the semaphore is released.
I have 2 background services.
Both of them have a call to a method that is NOT async. This method I cannot control - I can wrap it, sure.
I wire the backgroundservice(s) up by adding them to the servicecollection:
services.AddSingleton<IHostedService, BS1>();
services.AddSingleton<IHostedService, BS2>();
The execute async looks like this in each of them.
protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
log.LogInformation($"Start consuming from topic: {eventStreamConsumer.Topic}");
while (stoppingToken.IsCancellationRequested == false)
{
try
{
async...
var consumeResult = eventStreamConsumer.Consume();
The Consume method is blocking the thread here since it's not async.
My approach so far has been to wrap the inner workings of execute async into a Task.Factory.Start, but if the Consume does not return, the thread still hangs.
I would like to truly run this on a separate thread, but bear in mind the execute async has dependencies on other instances in the class - don't know if the will be a problem?
How does this approach look like?
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
var task = new Task(() =>
{
while (stoppingToken.IsCancellationRequested == false)
{
try
{
var consumeResult = eventStreamConsumer.Consume();
....
}
catch (Exception e)
{
//swallow
}
}
}, stoppingToken, TaskCreationOptions.LongRunning);
task.Start();
return Task.FromResult<object>(null);
}
The way async works is exactly that it returns a Task. That's the part you're missing: instead of waiting for the task to finish or reading its Result, return the task itself.
Also, don't use new Task followed by Task.Start. Task.Run is what you actually want pretty much every time (the only exception being if you're creating your own task scheduler).
I am developing android messanger app based on xamarin and .net 5 async/awaits.
In my app i have producer/consumer pattern for processing messages which is made on infinite loops.
for example ReadTcpClientAsync producer:
async Task ReadTcpClientAsync(CancellationToken cancellationToken)
{
cde.Signal();
while (!cancellationToken.IsCancellationRequested)
{
byte[] buffer = await atc.ReadAsync(cancellationToken);
// queue message...
}
}
or SendStatementsAsync consumer which deque messages and awaits WriteAsync
private async Task SendStatementsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var nextItem = await _outputStatements.Take();
cancellationToken.ThrowIfCancellationRequested();
// misc ...
await atc.WriteAsync(call.Serialize());
}
}
and some consumers just await on Take calls
var update = await _inputUpdateStatements.Take();
this construction works pretty well on tests, but there is one method where i think i made a huge mistake.
this method intent to run entire client backend, starting 3 pro/con while (true) loops simultaneously.
here it is:
public async Task RunAsync()
{
_isRunning = true;
_progress.ProgressChanged += progress_ProgressChanged;
await InitMTProto(_scheme).ConfigureAwait(false); // init smth...
// various init stuf...
await atc.ConnectAsync().ConfigureAwait(false); // open connection async
// IS IT WRONG?
try
{
await Task.WhenAny(SendStatementsAsync(_cts.Token),
ReadTcpClientAsync(_cts.Token),
ProcessUpdateAsync(_cts.Token, _progress)).ConfigureAwait(false);
}
catch (OperationCanceledException oce)
{
}
catch (Exception ex)
{
}
}
Forget about android for now, think any UI (WinForm, WPF, etc) OnCreate method in UI context to call RunAsync
protected async override void OnCreate(Bundle bundle)
{
// start RA
await client.RunAsync()
// never gets here - BAD, but nonblock UI thread - good
Debug.WriteLine("nevar");
}
so, as you can see there is a problem. I can't do anything after RunAsync await call because it will never returns from Task.WhenAny(...). And i need perform status check there, but i need this pro/cons methods started, because my check wait on ManualResetEvent for it:
if (!cde.Wait(15000))
{
throw new TimeoutException("Init too long");
}
Also, my check is async too, and it works like a charm :)
public async Task<TLCombinatorInstance> PerformRpcCall(string combinatorName, params object[] pars)
{
// wait for init on cde ...
// prepare call ...
// Produce
ProduceOutput(call);
// wait for answer
return await _inputRpcAnswersStatements.Take();
}
I think i should use another approach for starting this infinite loops, but i already have async Task methods all the way - so i really have no idea what to do.
Any help please?
Ok, after a lot of reading (nothing found) and #svick's advice i decided to call this methods without "await" as separate Task.Run's.
Aso i decided to run it in ThreadPool.
My final code is:
try
{
/*await Task.WhenAny(SendStatementsAsync(_cts.Token),
ReadTcpClientAsync(_cts.Token),
ProcessUpdateAsync(_cts.Token, _progress)).ConfigureAwait(false);*/
Task.Run(() => SendStatementsAsync(_cts.Token)).ConfigureAwait(false);
Task.Run(() => ReadTcpClientAsync(_cts.Token)).ConfigureAwait(false);
Task.Run(() => ProcessUpdateAsync(_cts.Token, _progress)).ConfigureAwait(false);
Trace.WriteLineIf(clientSwitch.TraceInfo, "Worker threads started", "[Client.RunAsync]");
}
Everything works fine as expected..
i'm not sure what problems it will cause in exception handling, as i know they will be lost
Of course such calls produce warning
Because this call is not awaited, execution of the current method
continues before the call is completed. Consider applying the 'await'
operator to the result of the call.
which can be easily suppressed this way
// just save task into variable
var send = Task.Run(() => SendStatementsAsync(_cts.Token)).ConfigureAwait(false);
Also, if anyone know better solution i will be grateful to hear it.