TPL inside Windows Service - c#

I need to perform few tasks inside a Windows Service I am writing in parallel. I am using VS2013, .NET 4.5 and this thread Basic design pattern for using TPL inside windows service for C# shows that TPL is the way to go.
Below is my implementation. I was wondering if anyone can tell me if I have done it correctly!
public partial class FtpLink : ServiceBase
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private readonly ManualResetEvent _runCompleteEvent = new ManualResetEvent(false);
public FtpLink()
{
InitializeComponent();
// Load configuration
WebEnvironment.Instance.Initialise();
}
protected override void OnStart(string[] args)
{
Trace.TraceInformation("DatabaseToFtp is running");
try
{
RunAsync(_cancellationTokenSource.Token).Wait();
}
finally
{
_runCompleteEvent.Set();
}
}
protected override void OnStop()
{
Trace.TraceInformation("DatabaseToFtp is stopping");
_cancellationTokenSource.Cancel();
_runCompleteEvent.WaitOne();
Trace.TraceInformation("DatabaseToFtp has stopped");
}
private async Task RunAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
Trace.TraceInformation("Working");
// Do the actual work
var tasks = new List<Task>
{
Task.Factory.StartNew(() => new Processor().ProcessMessageFiles(), cancellationToken),
Task.Factory.StartNew(() => new Processor().ProcessFirmware(), cancellationToken)
};
Task.WaitAll(tasks.ToArray(), cancellationToken);
// Delay the loop for a certain time
await Task.Delay(WebEnvironment.Instance.DatabasePollInterval, cancellationToken);
}
}
}

There are a few things i would do differently:
OnStart should execute in a timely fashion. Common practice is to defer work to a background thread which is in charge of doing the actual work. You're actually doing that but blocking that thread with a call to Task.Wait, which kind of makes the offloading to a background thread useless, because execution becomes synchronous again.
You're using the sync over async anti-pattern, this should be mostly avoided. Let the calling method invoke the work in parallel.
I think you might be using the ManualResetEvent the other way around. You're wrapping your RunAsync method in a try-finally block, but you're only calling WaitOne from OnStop. I'm not really sure you need a lock here at all, it doesn't seem (from your current code) that this code is being invoked in parallel. Instead, you can store the Task returned by RunAsync in a field and wait on it to complete.
You're using the blocking version, WaitAll. Instead, you could use the asynchronous version, Task.WhenAll, which can be asynchronously waited.

Related

Run multiple asynchronous Tasks continuously

I would like to implement a pool of a predetermined number (let's say 10) asynchronous tasks running undefinitely.
Using Task.WhenAll, I can easily start 10 tasks, feed them into a list, and call await Task.WhenAll(list) on this list. Once the method comes back, I can start again the whole process on the next 10 elements. The problem I face with this solution is that it waits for the longest task to complete before looping, which is not optimal.
What I would like is that anytime a task is completed, a new one is started. A timeout would be great as well, to prevent a task from being run undefinitely in case of a failure.
Is there any simple way of doing this?
What I would like is that anytime a task is completed, a new one is started.
This is a perfect use case for SemaphoreSlim:
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(10);
public async Task AddTask(Func<Task> work)
{
await _mutex.WaitAsync();
try { await work(); }
finally { _mutex.Release(); }
}
A timeout would be great as well, to prevent a task from being run undefinitely in case of a failure.
The standard pattern for timeouts is to use a CancellationTokenSource as the timer and pass a CancellationToken into the work that needs to support cancellation:
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(10);
public async Task AddTask(Func<CancellationToken, Task> work)
{
await _mutex.WaitAsync();
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
try { await work(cts.Token); }
finally { _mutex.Release(); }
}

Blocking call in BackgroundService makes yet another BackgroundService not run

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).

Locking issue with LimitedConcurrencyLevelTaskScheduler and aync/await

I'm struggling to understand what's happening in this simple program.
In the example below I have a task factory that uses the LimitedConcurrencyLevelTaskScheduler from ParallelExtensionsExtras with maxDegreeOfParallelism set to 2.
I then start 2 tasks that each call an async method (e.g. an async Http request), then gets the awaiter and the result of the completed task.
The problem seem to be that Task.Delay(2000) never completes. If I set maxDegreeOfParallelism to 3 (or greater) it completes. But with maxDegreeOfParallelism = 2 (or less) my guess is that there is no thread available to complete the task. Why is that?
It seems to be related to async/await since if I remove it and simply do Task.Delay(2000).GetAwaiter().GetResult() in DoWork it works perfectly. Does async/await somehow use the parent task's task scheduler, or how is it connected?
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks.Schedulers;
namespace LimitedConcurrency
{
class Program
{
static void Main(string[] args)
{
var test = new TaskSchedulerTest();
test.Run();
}
}
class TaskSchedulerTest
{
public void Run()
{
var scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
var taskFactory = new TaskFactory(scheduler);
var tasks = Enumerable.Range(1, 2).Select(id => taskFactory.StartNew(() => DoWork(id)));
Task.WaitAll(tasks.ToArray());
}
private void DoWork(int id)
{
Console.WriteLine($"Starting Work {id}");
HttpClientGetAsync().GetAwaiter().GetResult();
Console.WriteLine($"Finished Work {id}");
}
async Task HttpClientGetAsync()
{
await Task.Delay(2000);
}
}
}
Thanks in advance for any help
await by default captures the current context and uses that to resume the async method. This context is SynchronizationContext.Current, unless it is null, in which case it is TaskScheduler.Current.
In this case, await is capturing the LimitedConcurrencyLevelTaskScheduler used to execute DoWork. So, after starting the Task.Delay both times, both of those threads are blocked (due to the GetAwaiter().GetResult()). When the Task.Delay completes, the await schedules the remainder of the HttpClientGetAsync method to its context. However, the context will not run it since it already has 2 threads.
So you end up with threads blocked in the context until their async methods complete, but the async methods cannot complete until there is a free thread in the context; thus a deadlock. Very similar to the standard "don't block on async code" style of deadlock, just with n threads instead of one.
Clarifications:
The problem seem to be that Task.Delay(2000) never completes.
Task.Delay is completing, but the await cannot continue executing the async method.
If I set maxDegreeOfParallelism to 3 (or greater) it completes. But with maxDegreeOfParallelism = 2 (or less) my guess is that there is no thread available to complete the task. Why is that?
There are plenty of threads available. But the LimitedConcurrencyTaskScheduler only allows 2 threads at a time to run in its context.
It seems to be related to async/await since if I remove it and simply do Task.Delay(2000).GetAwaiter().GetResult() in DoWork it works perfectly.
Yes; it's the await that is capturing the context. Task.Delay does not capture a context internally, so it can complete without needing to enter the LimitedConcurrencyTaskScheduler.
Solution:
Task schedulers in general do not work very well with asynchronous code. This is because task schedulers were designed for Parallel Tasks rather than asynchronous tasks. So they only apply when code is running (or blocked). In this case, LimitedConcurrencyLevelTaskScheduler only "counts" code that's running; if you have a method that's doing an await, it won't "count" against that concurrency limit.
So, your code has ended up in a situation where it has the sync-over-async antipattern, probably because someone was trying to avoid the problem of await not working as expected with limited concurrency task schedulers. This sync-over-async antipattern has then caused the deadlock problem.
Now, you could add in more hacks by using ConfigureAwait(false) everywhere and continue blocking on asynchronous code, or you could fix it better.
A more proper fix would be to do asynchronous throttling. Toss out the LimitedConcurrencyLevelTaskScheduler completely; concurrency-limiting task schedulers only work with synchronous code, and your code is asynchronous. You can do asynchronous throttling using SemaphoreSlim, as such:
class TaskSchedulerTest
{
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(2);
public async Task RunAsync()
{
var tasks = Enumerable.Range(1, 2).Select(id => DoWorkAsync(id));
await Task.WhenAll(tasks);
}
private async Task DoWorkAsync(int id)
{
await _mutex.WaitAsync();
try
{
Console.WriteLine($"Starting Work {id}");
await HttpClientGetAsync();
Console.WriteLine($"Finished Work {id}");
}
finally
{
_mutex.Release();
}
}
async Task HttpClientGetAsync()
{
await Task.Delay(2000);
}
}
I think you are encountering a sync deadlock. You are waiting for a thread to complete that is waiting for your thread to complete. Never going to happen. If you make your DoWork method async so you can await the HttpClientGetAsync() call, and you'll avoid the deadlock.
using MassTransit.Util;
using System;
using System.Linq;
using System.Threading.Tasks;
//using System.Threading.Tasks.Schedulers;
namespace LimitedConcurrency
{
class Program
{
static void Main(string[] args)
{
var test = new TaskSchedulerTest();
test.Run();
}
}
class TaskSchedulerTest
{
public void Run()
{
var scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
var taskFactory = new TaskFactory(scheduler);
var tasks = Enumerable.Range(1, 2).Select(id => taskFactory.StartNew(() => DoWork(id)));
Task.WaitAll(tasks.ToArray());
}
private async Task DoWork(int id)
{
Console.WriteLine($"Starting Work {id}");
await HttpClientGetAsync();
Console.WriteLine($"Finished Work {id}");
}
async Task HttpClientGetAsync()
{
await Task.Delay(2000);
}
}
}
https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
TLDR never call .result, which I'm sure .GetResult(); was doing

Windows Service running Async code not waiting on work to complete

In Brief
I have a Windows Service that executes several jobs as async Tasks in parallel. However, when the OnStop is called, it seems that these are all immediately terminated instead of being allowed to stop in a more gracious manner.
In more detail
Each job represents an iteration of work, so having completed its work the job then needs to run again.
To accomplish this, I am writing a proof-of-concept Windows Service that:
runs each job as an awaited async TPL Task (these are all I/O bound tasks)
each job is run iteratively within a loop
each job's loop is run in parallel
When I run the Service, I see everything executing as I expect. However, when I Stop the service, it seems that everything stops dead.
Okay - so how is this working?
In the Service I have a cancellation token, and a TaskCompletion Source:
private static CancellationTokenSource _cancelSource = new CancellationTokenSource();
private TaskCompletionSource<bool> _jobCompletion = new TaskCompletionSource<bool>();
private Task<bool> AllJobsCompleted { get { return _finalItems.Task; } }
The idea is that when every Job has gracefully stopped, then the Task AllJobsCompleted will be marked as completed.
The OnStart simply starts running these jobs:
protected override async void OnStart(string[] args)
{
_cancelSource = new CancellationTokenSource();
var jobsToRun = GetJobsToRun(); // details of jobs not relevant
Task.Run(() => this.RunJobs(jobsToRun, _cancelSource.Token).ConfigureAwait(false), _cancelSource.Token);
}
The Task RunJobs will run each job in a parallel loop:
private async Task RunModules(IEnumerable<Jobs> jobs, CancellationToken cancellationToken)
{
var parallelOptions = new ParallelOptions { CancellationToken = cancellationToken };
int jobsRunningCount = jobs.Count();
object lockObject = new object();
Parallel.ForEach(jobs, parallelOptions, async (job, loopState) =>
{
try
{
do
{
await job.DoWork().ConfigureAwait(false); // could take 5 seconds
parallelOptions.CancellationToken.ThrowIfCancellationRequested();
}while(true);
}
catch(OperationCanceledException)
{
lock (lockObject) { jobsRunningCount --; }
}
});
do
{
await Task.Delay(TimeSpan.FromSeconds(5));
} while (modulesRunningCount > 0);
_jobCompletion.SetResult(true);
}
So, what should be happening is that when each job finishes its current iteration, it should see that the cancellation has been signalled and it should then exit the loop and decrement the counter.
Then, when jobsRunningCount reaches zero, then we update the TaskCompletionSource. (There may be a more elegant way of achieving this...)
So, for the OnStop:
protected override async void OnStop()
{
this.RequestAdditionalTime(100000); // some large number
_cancelSource.Cancel();
TraceMessage("Task cancellation requested."); // Last thing traced
try
{
bool allStopped = await this.AllJobsCompleted;
TraceMessage(string.Format("allStopped = '{0}'.", allStopped));
}
catch (Exception e)
{
TraceMessage(e.Message);
}
}
What what I expect is this:
Click [STOP] on the Service
The Service should take sometime to stop
I should see a trace statement "Task cancellation requested."
I should see a trace statement saying either "allStopped = true", or the exception message
And when I debug this using a WPF Form app, I get this.
However, when I install it as a service:
Click [STOP] on the Service
The Service stops almost immediately
I only see the trace statement "Task cancellation requested."
What do I need to do to ensure the OnStop doesn't kill off my parallel async jobs and waits for the TaskCompletionSource?
Your problem is that OnStop is async void. So, when it does await this.AllJobsCompleted, what actually happens is that it returns from OnStop, which the SCM interprets as having stopped, and terminates the process.
This is one of the rare scenarios where you'd need to block on a task, because you cannot allow OnStop to return until after the task completes.
This should do it:
protected override void OnStop()
{
this.RequestAdditionalTime(100000); // some large number
_cancelSource.Cancel();
TraceMessage("Task cancellation requested."); // Last thing traced
try
{
bool allStopped = this.AllJobsCompleted.GetAwaiter().GetResult();
TraceMessage(string.Format("allStopped = '{0}'.", allStopped));
}
catch (Exception e)
{
TraceMessage(e.Message);
}
}

Cooperative multitasking using TPL

We are porting modeling application, which uses IronPython scripts for custom actions in modeling process. The existing application executes each Python script in separate thread and uses cooperative model for this. Now we want to port it to TPL, but first we want to measure context switching
.
Basically, what we have right now:
Tasks queue
each Task from this queue executes one IronPython script
Inside IronPython script we call for C# class method, which is synchronization point and should transfer Task (IronPython execution) to waiting state
What we want to do:
We want to make infinite loop, which will iterate through Tasks queue
when we get one Task we try to execute it
In PythonScript we want to call C# method and transfer this script to waiting state. but not remove it from the queue.
On next iteration when we get another Task we check is it in the waiting state. if so we wake it up and try to execute.
In each moment we should have only one active Task
And finally we want to measure how many Task we could execute per second
I don't really know is it something about cooperative multitasking?
We are thinking about custom TaskScheduler, is it good approach? Or does someone know better solution?
Thanks.
Updated:
Ok ,so for example, I have such code:
public class CooperativeScheduler : TaskScheduler, IDisposable
{
private BlockingCollection<Task> _tasks;
private Thread _thread;
private Task _currentTask;
public CooperativeScheduler()
{
this._tasks = new BlockingCollection<Task>();
this._thread = new Thread(() =>
{
foreach (Task task in this._tasks.GetConsumingEnumerable())
{
this._currentTask = task;
TryExecuteTask(this._currentTask);
}
}
);
this._thread.Name = "Cooperative scheduler thread";
this._thread.Start();
}
public void SleepCurrentTask()
{
if (this._currentTask != null)
{
// what to do here?
}
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return this._tasks.ToArray<Task>();
}
protected override void QueueTask(Task task)
{
// No long task
this._tasks.Add(task);
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
throw new NotImplementedException();
}
public void Dispose()
{
this._tasks.CompleteAdding();
this._thread.Join();
}
}
Custom Task Scheduler, it has one _thread for Task execution and _currentTask field for running task, also it has SleepCurrentTask in this method I want to suspend current Task execution, but I don't know how.
Client code is simple:
CancellationTokenSource tokenSource = new CancellationTokenSource();
Application app = Application.Create();
Task task = Task.Factory.StartNew(() =>
{
app.Scheduler.SleepCurrentTask();
},
tokenSource.Token, TaskCreationOptions.None, app.Scheduler);
}
Maybe someone has better ideas?
It sounds like you'd be well served to use the producer/consumer pattern .NET 4 has built in to a few collections.
Check out page 55 in this free PDF from Microsoft, Patterns of Parallel Programming

Categories