Pattern for cyclic calls of async operations - c#

I want to do some I/O based async operations periodically.
It should not run as fast as possible but with a configurable delay between the cycles.
So far I came up with two different approaches and I am wondering which one is better in regards of ressource consumption.
Approach 1 with Task.Run()
internal class Program
{
private static void Main(string[] args)
{
for (var i = 0; i < 80; i++)
{
var handler = new CommunicationService();
handler.Start();
}
Console.ReadLine();
}
}
internal class CommunicationService
{
private readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler());
public void Start()
{
Run();
}
private void Run()
{
Task.Run(async () =>
{
try
{
var result = await _httpClient.GetAsync(someUri);
await Task.Delay(TimeSpan.FromSeconds(configurableValue));
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
Run();
}
Run();
});
}
}
So the async operation is wrapped in a Task.Run() in a fire and forget style, so it can be started without blocking.
Approach 2 with EventHandler
internal class CommunicationService
{
private event EventHandler CommunicationHandler;
private readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler());
public void Start()
{
CommunicationHandler = (o, events) => Communicate();
OnCommunicationTriggered();
}
private async void Communicate()
{
try
{
var result = await _httpClient.GetAsync(someUri);
await Task.Delay(TimeSpan.FromSeconds(configurableValue));
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
OnCommunicationTriggered();
}
OnCommunicationTriggered();
}
private void OnCommunicationTriggered()
{
CommunicationHandler.Invoke(this, EventArgs.Empty);
}
}
With this approach wrapping in Task.Run() is not necessary but is it therefore better?
I created a .Net console application for both approaches and recorded following performance counters over a few minutes and did not see that much difference to be honest:
\Process(Events)% Processor Time (approach 2 ~20 % higher)
\Process(Events)\Private Bytes (almost equal, approach 2 slighlty
lower)
\Process(Events)\Thread Count (approach 2 ~ 25% lower)
.NET CLR LocksAndThreads(Events)# of current logical Threads
(almost equal, approach 2 slighlty higher)
.NET CLR LocksAndThreads(Events)# of current physical Threads
(almost equal, approach 2 slighlty higher)
.NET CLR LocksAndThreads(Events)\Contention Rate / sec (approach 2
~ 50% higher)
Am I missing the point here with these counters?

Both are really doing the same thing. The event option seem to add a unneeded layer of complexity. There is not significant difference in resource consumption.
A more appropriate option would be to use a timer.timer or threading.timer. This makes the code easier to read and understand since it expresses intent. Behind the scene all of the alternatives result in more or less the same thing.
You will need to consider how you count the time. Should the execution time be included in the timing interval or not? Often the interval is much longer than the execution time, so it does not matter. If it does matter you might need to set the timer to only trigger once, and reset the timer once your operation has completed.

according to the accepted answer here is my new approach:
internal class EventHandlerService
{
private Timer _timer;
private TimeSpan refreshTime = TimeSpan.FromSeconds(5);
public void Start()
{
_timer = new Timer(Communicate, null, 0,(int)refreshTime.TotalMilliseconds);
}
private void Communicate(object stateInfo)
{
Task.Run(async () =>
{
_timer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); // stop the timer
Console.WriteLine($"Starting at {DateTime.UtcNow.ToString("O")}");
var stopWatch=new Stopwatch();
stopWatch.Start();
try
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
catch (Exception ex)
{
}
finally
{
Console.WriteLine($"Finishing at {DateTime.UtcNow.ToString("O")} after: {stopWatch.Elapsed}");
var dueTime = refreshTime.Subtract(stopWatch.Elapsed);
Console.WriteLine($"Calced dueTime to: {dueTime.TotalSeconds} at {DateTime.UtcNow.ToString("O")}");
_timer.Change(Math.Max((int) dueTime.TotalMilliseconds, 0), (int)refreshTime.TotalMilliseconds); // start the timer
}
});
}
}
with this approach I got my needs covered:
the actual refresh/timer period does never fall below 5 seconds but if the handler takes longer than 5 seconds, the next execution is being triggered without delay.

Related

How to properly delay when there is no task to wait for

I have a task that is waiting for a property to be set to true (= completed). The way I am receiving that property value change is via EventHandler (System.Diagnostics.Process.OutputDataReceived to be exact - it continuously reads the output of another process until the correct output is provided). However checking for the property all the time feels somewhat inefficiencient. I have tried adding a small delay of one tick because I believe I can allow myself such a wait if that would save CPU time, but I read .NET struggles with fractional milliseconds. Can I improve this code?
private ConcurrentBag<string> _allMessages = new ConcurrentBag<string>();
public OutputRetriever()
{
var process = new System.Diagnostics.Process();
...
process.OutputDataReceived += OutputDataReceived;
process.Start();
}
public async Task<string[]> GetAllOutput()
{
while (!IsCompleted)
{
// how to properly wait here?
// await Task.Delay(TimeSpan.FromTicks(1)); // is this ok?
}
return _allMessages.ToArray();
}
private void ConsoleDataReceived(object sender, DataReceivedEventArgs e)
{
_allMessages.Add(e?.Data);
if (e?.Data == "success")
{
IsCompleted = true;
}
}
The timers in Windows have a resolution of approx. 16 ms, so any delay below 16 ms cannot be precisely achieved. This applies to any timer - the .NET timers are just wrappers for Windows native timers.
Instead of busy-waiting in a loop, create a custom TaskCompletionSource<T> and return a Task that can be awaited.
class OutputRetriever
{
private readonly ConcurrentBag<string> _allMessages = new ConcurrentBag<string>();
private readonly TaskCompletionSource<string[]> _taskSource
= new TaskCompletionSource<string[]>();
// Note: this method is not async anymore
public Task<string[]> GetAllOutput()
{
// We just return a task that can be awaited
return _taskSource.Task;
}
void ConsoleDataReceived(object sender, DataReceivedEventArgs e)
{
_allMessages.Add(e?.Data);
if (e?.Data == "success")
{
// Here we notify that the task is completed by setting the result
_taskSource.SetResult(_allMessages.ToArray());
}
}
}
Now the clients can simply await the results as usual:
var receiver = new OutputReceiver();
string[] messages = await receiver.GetAllOutput();

Async/Await or Task.Run in Console Application/Windows Service

I have been researching (including looking at all other SO posts on this topic) the best way to implement a (most likely) Windows Service worker that will pull items of work from a database and process them in parallel asynchronously in a 'fire-and-forget' manner in the background (the work item management will all be handled in the asynchronous method). The work items will be web service calls and database queries. There will be some throttling applied to the producer of these work items to ensure some kind of measured approach to scheduling the work. The examples below are very basic and are just there to highlight the logic of the while loop and for loop in place. Which is the ideal method or does it not matter? Is there a more appropriate/performant way of achieving this?
async/await...
private static int counter = 1;
static void Main(string[] args)
{
Console.Title = "Async";
Task.Run(() => AsyncMain());
Console.ReadLine();
}
private static async void AsyncMain()
{
while (true)
{
// Imagine calling a database to get some work items to do, in this case 5 dummy items
for (int i = 0; i < 5; i++)
{
var x = DoSomethingAsync(counter.ToString());
counter++;
Thread.Sleep(50);
}
Thread.Sleep(1000);
}
}
private static async Task<string> DoSomethingAsync(string jobNumber)
{
try
{
// Simulated mostly IO work - some could be long running
await Task.Delay(5000);
Console.WriteLine(jobNumber);
}
catch (Exception ex)
{
LogException(ex);
}
Log("job {0} has completed", jobNumber);
return "fire and forget so not really interested";
}
Task.Run...
private static int counter = 1;
static void Main(string[] args)
{
Console.Title = "Task";
while (true)
{
// Imagine calling a database to get some work items to do, in this case 5 dummy items
for (int i = 0; i < 5; i++)
{
var x = Task.Run(() => { DoSomethingAsync(counter.ToString()); });
counter++;
Thread.Sleep(50);
}
Thread.Sleep(1000);
}
}
private static string DoSomethingAsync(string jobNumber)
{
try
{
// Simulated mostly IO work - some could be long running
Task.Delay(5000);
Console.WriteLine(jobNumber);
}
catch (Exception ex)
{
LogException(ex);
}
Log("job {0} has completed", jobNumber);
return "fire and forget so not really interested";
}
pull items of work from a database and process them in parallel asynchronously in a 'fire-and-forget' manner in the background
Technically, you want concurrency. Whether you want asynchronous concurrency or parallel concurrency remains to be seen...
The work items will be web service calls and database queries.
The work is I/O-bound, so that implies asynchronous concurrency as the more natural approach.
There will be some throttling applied to the producer of these work items to ensure some kind of measured approach to scheduling the work.
The idea of a producer/consumer queue is implied here. That's one option. TPL Dataflow provides some nice producer/consumer queues that are async-compatible and support throttling.
Alternatively, you can do the throttling yourself. For asynchronous code, there's a built-in throttling mechanism called SemaphoreSlim.
TPL Dataflow approach, with throttling:
private static int counter = 1;
static void Main(string[] args)
{
Console.Title = "Async";
var x = Task.Run(() => MainAsync());
Console.ReadLine();
}
private static async Task MainAsync()
{
var blockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 7
};
var block = new ActionBlock<string>(DoSomethingAsync, blockOptions);
while (true)
{
var dbData = await ...; // Imagine calling a database to get some work items to do, in this case 5 dummy items
for (int i = 0; i < 5; i++)
{
block.Post(counter.ToString());
counter++;
Thread.Sleep(50);
}
Thread.Sleep(1000);
}
}
private static async Task DoSomethingAsync(string jobNumber)
{
try
{
// Simulated mostly IO work - some could be long running
await Task.Delay(5000);
Console.WriteLine(jobNumber);
}
catch (Exception ex)
{
LogException(ex);
}
Log("job {0} has completed", jobNumber);
}
Asynchronous concurrency approach with manual throttling:
private static int counter = 1;
private static SemaphoreSlim semaphore = new SemaphoreSlim(7);
static void Main(string[] args)
{
Console.Title = "Async";
var x = Task.Run(() => MainAsync());
Console.ReadLine();
}
private static async Task MainAsync()
{
while (true)
{
var dbData = await ...; // Imagine calling a database to get some work items to do, in this case 5 dummy items
for (int i = 0; i < 5; i++)
{
var x = DoSomethingAsync(counter.ToString());
counter++;
Thread.Sleep(50);
}
Thread.Sleep(1000);
}
}
private static async Task DoSomethingAsync(string jobNumber)
{
await semaphore.WaitAsync();
try
{
try
{
// Simulated mostly IO work - some could be long running
await Task.Delay(5000);
Console.WriteLine(jobNumber);
}
catch (Exception ex)
{
LogException(ex);
}
Log("job {0} has completed", jobNumber);
}
finally
{
semaphore.Release();
}
}
As a final note, I hardly ever recommend my own book on SO, but I do think it would really benefit you. In particular, sections 8.10 (Blocking/Asynchronous Queues), 11.5 (Throttling), and 4.4 (Throttling Dataflow Blocks).
First of all, let's fix some.
In the second example you are calling
Task.Delay(5000);
without await. It is a bad idea. It creates a new Task instance which runs for 5 seconds but no one is waiting for it. Task.Delay is only useful with await. Mind you, do not use Task.Delay(5000).Wait() or you are going to get deadlocked.
In your second example you are trying to make the DoSomethingAsync method synchronous, lets call it DoSomethingSync and replace the Task.Delay(5000); with Thread.Sleep(5000);
Now, the second example is almost the old-school ThreadPool.QueueUserWorkItem. And there is nothing bad with it in case you are not using some already-async API inside. Task.Run and ThreadPool.QueueUserWorkItem used in the fire-and-forget case are just the same thing. I would use the latter for clarity.
This slowly drives us to the answer to the main question. Async or not async - this is the question! I would say: "Do not create async methods in case you do not have to use some async IO inside your code". If however there is async API you have to use than the first approach would be more expected by those who are going to read your code years later.

What is the best way to implement a Retry Wrapper in C#?

We currently have a naive RetryWrapper which retries a given func upon the occurrence of an exception:
public T Repeat<T, TException>(Func<T> work, TimeSpan retryInterval, int maxExecutionCount = 3) where TException : Exception
{
...
And for the retryInterval we are using the below logic to "wait" before the next attempt.
_stopwatch.Start();
while (_stopwatch.Elapsed <= retryInterval)
{
// do nothing but actuallky it does! lots of CPU usage specially if retryInterval is high
}
_stopwatch.Reset();
I don't particularly like this logic, also ideally I would prefer the retry logic NOT to happen on the main thread, can you think of a better way?
Note: I am happy to consider answers for .Net >= 3.5
So long as your method signature returns a T, the main thread will have to block until all retries are completed. However, you can reduce CPU by having the thread sleep instead of doing a manual reset event:
Thread.Sleep(retryInterval);
If you are willing to change your API, you can make it so that you don't block the main thread. For example, you could use an async method:
public async Task<T> RepeatAsync<T, TException>(Func<T> work, TimeSpan retryInterval, int maxExecutionCount = 3) where TException : Exception
{
for (var i = 0; i < maxExecutionCount; ++i)
{
try { return work(); }
catch (TException ex)
{
// allow the program to continue in this case
}
// this will use a system timer under the hood, so no thread is consumed while
// waiting
await Task.Delay(retryInterval);
}
}
This can be consumed synchronously with:
RepeatAsync<T, TException>(work, retryInterval).Result;
However, you can also start the task and then wait for it later:
var task = RepeatAsync<T, TException>(work, retryInterval);
// do other work here
// later, if you need the result, just do
var result = task.Result;
// or, if the current method is async:
var result = await task;
// alternatively, you could just schedule some code to run asynchronously
// when the task finishes:
task.ContinueWith(t => {
if (t.IsFaulted) { /* log t.Exception */ }
else { /* success case */ }
});
Consider using Transient Fault Handling Application Block
The Microsoft Enterprise Library Transient Fault Handling Application
Block lets developers make their applications more resilient by adding
robust transient fault handling logic. Transient faults are errors
that occur because of some temporary condition such as network
connectivity issues or service unavailability. Typically, if you retry
the operation that resulted in a transient error a short time later,
you find that the error has disappeared.
It is available as a NuGet package.
using Microsoft.Practices.TransientFaultHandling;
using Microsoft.Practices.EnterpriseLibrary.WindowsAzure.TransientFaultHandling;
...
// Define your retry strategy: retry 5 times, starting 1 second apart
// and adding 2 seconds to the interval each retry.
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2));
// Define your retry policy using the retry strategy and the Windows Azure storage
// transient fault detection strategy.
var retryPolicy =
new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy);
// Receive notifications about retries.
retryPolicy.Retrying += (sender, args) =>
{
// Log details of the retry.
var msg = String.Format("Retry - Count:{0}, Delay:{1}, Exception:{2}",
args.CurrentRetryCount, args.Delay, args.LastException);
Trace.WriteLine(msg, "Information");
};
try
{
// Do some work that may result in a transient fault.
retryPolicy.ExecuteAction(
() =>
{
// Your method goes here!
});
}
catch (Exception)
{
// All the retries failed.
}
How about using a timer instead of stopwatch?
For example:
TimeSpan retryInterval = new TimeSpan(0, 0, 5);
DateTime startTime;
DateTime retryTime;
Timer checkInterval = new Timer();
private void waitMethod()
{
checkInterval.Interval = 1000;
checkInterval.Tick += checkInterval_Tick;
startTime = DateTime.Now;
retryTime = startTime + retryInterval;
checkInterval.Start();
}
void checkInterval_Tick(object sender, EventArgs e)
{
if (DateTime.Now >= retryTime)
{
checkInterval.Stop();
// Retry Interval Elapsed
}
}

Replacing Thread.Suspend and Thread.Resume in a windows service

We have a service that does the following basic workflow:
1) Starts, reads config settings and performs some calculations in a large loop.
2) Each iteration of the loop, it needs to be able to check if the service has been told to stop. It performs database fetches, calculations then stores results. I am not confident on how well the code is done wrt SQL transactions so at this stage, happy to assume we are only checking for service stop at the start of each iteration.
3) After performing all iterations, the service "sleeps" for a period of time. Could be 5 minutes. Could be 12 hours. It needs to be able to "stop" in this sleep period!
Currently this is performed by the following:
private int threadSleepMinutes = 60;
private readonly Mutex mutTerminateService = new Mutex(false);
private Thread serviceThread;
private Thread serviceStopThread;
// Use this flag to allow the Start op to wait for serviceStopThread
// to get going before continuing to create the main loop thread
private volatile bool stopService = true;
public void Start()
{
this.serviceStopThread = new Thread(this.RunServiceStopThread);
this.serviceStopThread.IsBackground = true;
this.serviceStopThread.Start();
while (stopService)
{
Thread.Sleep(100);
}
// Some things renamed to anonymise... you get the idea!
this.serviceThread = new Thread(this.BigLoopMethod);
this.serviceThread.IsBackground = true;
this.serviceThread.Start();
}
public void Stop()
{
// Release the mutex to terminate the service
serviceStopThread.Resume();
// Wait 5s max
int timeout = 5000;
while (this.serviceThread.IsAlive && timeout > 0)
{
Thread.Sleep(100);
timeout -= 100;
}
}
private void RunServiceStopThread()
{
// To guarantee the same thread takes the mutex
// and releases it in dot net 4, do both ops in this single thread!
// Dot net 4 the Start() and Stop() are now usually on different threads.
mutTerminateService.WaitOne();
stopService = false;
// Suspend ourself
serviceStopThread.Suspend();
// Release the mutex
mutTerminateService.ReleaseMutex();
}
public void BigLoopMethod()
{
try
{
do
{
bool moreOperationsToGo = true; // Just dummy flags and 'stuff' methods here
while (moreOperationsToGo && !mutTerminateService.WaitOne(0))
{
DoStuff();
}
// Using this mutex here to sleep nicely - event driven.
// Gracefully continues after timeout and gracefully exits if
// triggered by the mutex.
}
while (!mutTerminateService.WaitOne(this.threadSleepMinutes * 60000));
}
catch (Exception ex)
{
// Exception handling & logging here
}
}
Now I get messages saying Suspend and Resume are deprecated. In my situation, I know exactly what code the suspend was run on since the call itself is what suspended it! Resume, I know exactly what it is going to do. The only reason this was even done in the first place was because the mutex worked fine in Start() and Stop() in dot net 3.5 but dot net 4.0 changed so that Start() and Stop() were in different threads AND they marked the workaround as obsolete!
Is there a nice way, non-obsolete way of doing this?
Thanks
Unless you are using mutex for inter-process communication, i.e. cancelling your worker thread from another process - I believe there is an easier way to implement a worker thread with cancellation in .net 4.0. You can use a cancellation token, and wait with timeout on it - it will signal if token was cancelled. Complete solution (partially using your code) below:
using System;
using System.Threading;
class App
{
static void Main()
{
var t = new Test();
t.Start();
Thread.Sleep(10000);
Console.WriteLine("aborting");
t.Stop();
}
}
class Test
{
private int threadSleepMinutes = 60;
private Thread serviceThread;
private CancellationTokenSource tokenSource;
public void Start()
{
// Some things renamed to anonymise... you get the idea!
this.tokenSource = new CancellationTokenSource();
this.serviceThread = new Thread(this.BigLoopMethod);
this.serviceThread.IsBackground = true;
this.serviceThread.Start();
}
public void Stop()
{
tokenSource.Cancel();
// Wait 5s max
int timeout = 5000;
if (!serviceThread.Join(timeout))
{
serviceThread.Abort();
}
}
public void BigLoopMethod()
{
try
{
var token = tokenSource.Token;
do
{
int operationsToGo = 4; // Just dummy flags and 'stuff' methods here
while (operationsToGo > 0 && !token.IsCancellationRequested)
{
Console.WriteLine("work");
Thread.Sleep(1000);//DoStuff();
operationsToGo--;
}
Console.WriteLine("no more work");
}
while (!token.WaitHandle.WaitOne(this.threadSleepMinutes * 60000));
}
catch (Exception ex)
{
// Exception handling & logging here
}
}
}
You don't need a "stop" thread. The fact that the start method triggers the BigLoopMethod will be sufficient. All you need in stop is to signal the mutex and then join the thread (Thread.Join() will wait for the thread to halt) with an appropriate timeout. I would recommend for robustness to thread abort if your thread doesn't join within an appropriate time to forcibly kill the service.
So in psuedo code:
void Start()
{
OpenMutex();
TakeMutex();
KickOffMyThread();
}
void Stop();
{
SignalMutex();
if (!MyThread.Join(Timeout))
{
MyThread.Abort();
Environment.Exit(1); // Die as thread won't join
}
}
void MyThread()
{
while (!TakeMutex(sleeptime)
{
DoLongWork();
}
//Thread was signalled, exiting.
}

Why does the process lose threads?

Here is some code that perpetually generate GUIDs. I've written it to learn about threading. In it you'll notice that I've got a lock around where I generate GUIDs and enqueue them even though the ConcurrentQueue is thread safe. It's because my actual code will need to use NHibernate and so I must make sure that only one thread gets to fill the queue.
While I monitor this code in Task Manager, I notice the process drops the number of threads from 18 (on my machine) to 14 but no less. Is this because my code isn't good?
Also can someone refactor this if they see fit? I love shorter code.
class Program
{
ConcurrentNewsBreaker Breaker;
static void Main(string[] args)
{
new Program().Execute();
Console.Read();
}
public void Execute()
{
Breaker = new ConcurrentNewsBreaker();
QueueSome();
}
public void QueueSome()
{
ThreadPool.QueueUserWorkItem(DoExecute);
}
public void DoExecute(Object State)
{
String Id = Breaker.Pop();
Console.WriteLine(String.Format("- {0} {1}", Thread.CurrentThread.ManagedThreadId, Breaker.Pop()));
if (Breaker.Any())
QueueSome();
else
Console.WriteLine(String.Format("- {0} XXXX ", Thread.CurrentThread.ManagedThreadId));
}
}
public class ConcurrentNewsBreaker
{
static readonly Object LockObject = new Object();
ConcurrentQueue<String> Store = new ConcurrentQueue<String>();
public String Pop()
{
String Result = null;
if (Any())
Store.TryDequeue(out Result);
return Result;
}
public Boolean Any()
{
if (!Store.Any())
{
Task FillTask = new Task(FillupTheQueue, Store);
FillTask.Start();
FillTask.Wait();
}
return Store.Any();
}
private void FillupTheQueue(Object StoreObject)
{
ConcurrentQueue<String> Store = StoreObject as ConcurrentQueue<String>;
lock(LockObject)
{
for(Int32 i = 0; i < 100; i++)
Store.Enqueue(Guid.NewGuid().ToString());
}
}
}
You are using .NET's ThreadPool so .NET/Windows manages the number of threads based on the amount of work waiting to be processed.
While I monitor this code in Task
Manager, I notice the process drops
the number of threads from 18 (on my
machine) to 14 but no less. Is this
because my code isn't good?
This does not indicate a problem. 14 is still high, unless you've got a 16-core cpu.
The threadpool will try to adjust and do the work with as few threads as possible.
You should start to worry when the number of threads goes up significantly.

Categories