execute x number of Actions each every n milliseconds - c#

I have a program where I let the user create several functions and once he creates all the functions I run them every x milliseconds. In other words I have something like:
// functionsToExecute is of type = List<Action>
// x = some integer
while(true){
foreach(Action action in functionsToExecute)
{
action();
}
Thread.Sleep(x);
}
Now I will like for the user to decide how long to wait per function. For example if the user creates 2 functions he might want the first function to run every 500 milliseconds the next one every 1500. I was thinking about creating two threads for this scenario and then have the same implementation. But what if the user creates 50 functions? I will need 50 threads!
In short I will like to execute x number of Actions each every n milliseconds... What will be the best way to create such algorithm? For example if I have 3 Actions I will like to execute the first action every 200 milliseconds, the next one every 500 milliseconds and the last one every 1000 milliseconds.
Maybe I need something similar to the SetTimout function in javascript

If you're using .NET 4.5 and your code is not time-critical, then you can easily do this with the Task Parallel Library:
static Task Repeat (List<Action> actions, CancellationToken token, int delay)
{
var tasks = new List<Task> ();
var cts = CancellationTokenSource.CreateLinkedTokenSource (token);
foreach (var action in actions) {
var task = Task.Factory.StartNew (async () => {
while (true) {
cts.Token.ThrowIfCancellationRequested ();
await Task.Delay (delay, cts.Token).ConfigureAwait (false);
action ();
}
});
tasks.Add (task);
}
return Task.WhenAll (tasks);
}
Ideally, you should also make your actions async to properly support cancellation.
The .NET runtime automatically takes care of thread scheduling, but there's no guarantee that your action will be executed after exactly the requested timeout. It will be executed after at least that time has elapsed and there's an idle thread available.

i would consider using a ThreadPool (walkthrough). Create each thread to process and have it repeat based on the timeout they're looking for. You can also store the ManualResetEvent for when you need the thread(s) to stop.

Related

How can I make sure my tests start and run correctly?

I'm determining between using TPL Dataflow blocks or some sort of producer/consumer approach for these tests. Producing a list of tasks will be super-fast, as each task will just be a string containing a list of test parameters such as the setup parameters, the measurements required, and time between measurements. This list of tasks will simply be files loaded through the GUI (1 file per test).
When at test is started, it should start right away. The tests could be very long and very asynchronous in that an action could take seconds or tens of minutes (e.g. heating up a device), followed by a measurement that takes a few seconds (or minutes), followed by a long period of inaction (24 hours) before the test is repeated again.
I could have up to 16 tests running at the same time, but I need the flexibility to be able to cancel any one of those tests at any time. I also need to be able to ADD a new test at any time (i.e. try to picture testing of 16 devices, or the span of a month in which individual test devices are added and removed throughout the month).
(Visual C#) I tried this example code for TPL dataflow where I tell it to run 32 simple tasks all at the same time. Each task is just a 5 second delay to simulate work. It appears to be processing the tasks in parallel as the time to complete the tasks took 15 seconds. I assume all 32 tasks did not finish in 5 seconds due to scheduling and any other overhead, but I am a bit worried that some task might of been blocked.
class Program
{
// Performs several computations by using dataflow and returns the elapsed
// time required to perform the computations.
static TimeSpan TimeDataflowComputations(int messageCount)
{
// Create an ActionBlock<int> that performs some work.
var workerBlock = new ActionBlock<int>(
// Simulate work by suspending the current thread.
millisecondsTimeout => Thread.Sleep(millisecondsTimeout),
// Specify a maximum degree of parallelism.
new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = messageCount
});
// Compute the time that it takes for several messages to
// flow through the dataflow block.
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < messageCount; i++)
{
workerBlock.Post(5000); // simulated work: a delay of 5 seconds.
}
workerBlock.Complete();
// Wait for all messages to propagate through the network.
workerBlock.Completion.Wait();
// Stop the timer and return the elapsed number of milliseconds.
stopwatch.Stop();
return stopwatch.Elapsed;
}
static void Main(string[] args)
{
int messageCount = 32;
TimeSpan elapsed;
// set processors maximum degree of parallelism. This causes
// multiple messages to be processed in parallel.
Console.WriteLine("START:\r\n");
elapsed = TimeDataflowComputations(messageCount);
Console.WriteLine("message count = {1}; " +
"elapsed time = {2}ms.", messageCount,
(int)elapsed.TotalMilliseconds);
Console.ReadLine();
}
}
The demo seems to work, but I am not sure if any of the tasks were blocked until one or more of the 5 second tasks were completed. I am also not sure how one would go about identifying each action block in order to cancel a specific one.
The reason that you don't get the expected performance is because your workload is synchronous and blocks the thread-pool threads. Do you expect to actually have synchronous (blocking) workload in your production environment? If yes, you could try boosting the ThreadPool reserve of available threads before starting the TPL Dataflow pipeline:
ThreadPool.SetMinThreads(workerThreads: 100, completionPortThreads: 100);
If your actual workload is asynchronous, then you could better simulate it with Task.Delay instead of Thread.Sleep.
var workerBlock = new ActionBlock<int>(async millisecondsTimeout =>
{
await Task.Delay(millisecondsTimeout);
}, new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = messageCount
});
I didn't test it, but you should get completion times at around 5 sec with both these approaches.

How to increase priority of a Task in .NET?

I have a program that generates txt files with randomized contents. Many documents have to be generated, so I use Tasks to get the work done faster. I want to notify the user of the progress that's being made every few seconds (ex: "Generated 50000 documents out of 100000"). I create another Task, RecordProgress(), which records progress every 3 seconds.
However, if the program generates many tasks, the RecordProgress() never runs. If the program only generates 4 tasks, then RecordProgress() runs correctly ie. gets called every 3 seconds. However, if the program generates many tasks, RecordProgress() only runs once processing is/close to being finished.
Is there any way to increase the priority of the RecordProgress() task?
I've tried logging progress in each task, but that generates too many log messages to the console, which severely slows down my program.
I've tried logging in each task and waiting 3 seconds between logs, but if the program generates 50 tasks, then 50 messages will be logged to the console at the same time, which once again slows down my program and is unnecessary. I'd rather have ONE log message to the console every 3 seconds.
public void RecordProgress()
{
Stopwatch sw = new Stopwatch();
sw.Start();
//only record data while this generator is generating
while (_processing)
{
if(sw.ElapsedMilliseconds < _logFrequency)
continue;
Console.WriteLine("Generated " + _docsGenerated + " documents.");
sw.Restart();
}
}
public void GenerateDocs()
{
List<Task> tasks = new List<Task>();
_processing = true;
for (i = 0; i < 50; i ++)
{
tasks.Add(Task.Run(() => DoWork());
}
//task which records progress
//ONLY runs if not many tasks are created above
Task.Run(() => RecordProgress());
Task.WaitAll(tasks.ToArray());
}
I'd like the RecordProgress() task to run every 3 seconds regardless of the number of tasks generated by this program.
Edit: As per the comments, I removed the use of Thread.Sleep(). However, that only delayed the starting of my RecordProgress() task.
I've attempted to use a Stopwatch in RecordProgress() to only record progress every 3 seconds, but it greatly slows the performance of my program.
So new question: how to record progress of tasks without using a timer that heavily impacts performance?
In the original case, you create many tasks and exhaust the available threads in the Thread Pool. Running ReportProgress last delays its execution until most of the other tasks are complete. I see you corrected you code.
As for the priority question. Have in mind that you cannot change the priority of a task. You may achieve something like it by implementing your own TaskScheduler, but by default all tasks run a normal priority.
If you really need to run a task in higher priority, you need to create a Thread and set its priority to higher / highest. Something you should be very careful about.
I've found it:
I've created a Stopwatch object that requires the use of a lock to access. At the end of my DoWork() task, the task locks and checks how much time has passed since the program last logged. If over 3 seconds have passed, the task logs progress and resets the Stopwatch object. The RecordProgress() task is no longer necessary.
public void GenerateDocs()
{
List<Task> tasks = new List<Task>();
lock (_lockForLog)
{
_swForLogging.Start();
}
_processing = true;
for (i = 0; i < 50; i ++)
{
tasks.Add(Task.Run(() => DoWork());
}
Task.WaitAll(tasks.ToArray());
lock (_lockForLog)
{
_swForLogging.Stop();
}
}
public void DoWork(){
//do work
lock (_lockForLog)
{
if (_swForLogging.ElapsedMilliseconds > 3000)
{
Console.WriteLine("Generated " + _docsGenerated + " documents.");
_swForLogging.Restart();
}
}
}

CancellationTokenSource not working properly with Parallel.ForEach

I have the following code:
CancellationTokenSource ts = new CancellationTokenSource(10000);
ParallelOptions po = new ParallelOptions();
po.CancellationToken = ts.Token;
List<int> lItems = new List<int>();
for (int i = 0; i < 20; i++)
lItems.Add(i);
System.Collections.Concurrent.ConcurrentBag<int> lBgs = new System.Collections.Concurrent.ConcurrentBag<int>();
Stopwatch sp = Stopwatch.StartNew();
try
{
Parallel.ForEach(lItems, po, i =>
{
Task.Delay(i * 1000).Wait();
lBgs.Add(i);
});
}
catch (Exception ex)
{
}
Console.WriteLine("Elapsed time: {0:N2} seg Total items: {1}", sp.ElapsedMilliseconds / 1000.0, lBgs.Count);
My question is why takes more than 20 sec to cancel the operation (parallel for) if the CancelationTokenSource is set to finish in 10 sec
Regards
Without a good Minimal, Complete, and Verifiable code example, it's impossible to fully understand your scenario. But based on the code you posted, it appears that you expect for your CancellationToken to affect the execution of each individual iteration of the Parallel.ForEach().
However, that's not how it works. The Parallel.ForEach() method schedules individual operations concurrently, but once those operations start, they are out of the control of the Parallel.ForEach() method. If you want them to terminate early, you have to do that yourself. E.g.:
Parallel.ForEach(lItems, po, i =>
{
Task.Delay(i * 1000, ts.Token).Wait();
lBgs.Add(i);
});
As your code stands now, all 20 actions are started almost immediately (there's a short delay as the thread pool creates enough threads for all the actions, if necessary), before you cancel the token. That is, by the time you cancel the token, the Parallel.ForEach() method no longer has a way to avoid starting the actions; they are already started!
Since your individual actions don't do anything to interrupt themselves, then all that's left is for them all to complete. The start-up time (including waiting for the thread pool to create enough worker threads), plus the longest total delay (i.e. the delay to start an action plus that action's delay), determines the total time the operation takes, with your cancellation token having no effect. Since your longest action is 20 seconds, the total delay for the Parallel.ForEach() operation will always be at least 20 seconds.
By making the change I show above, the delay task for each individual action will be cancelled by your token when it expires, causing a task-cancelled exception. This will cause the action itself to terminate early as well.
Note that there is still value in assigning the cancellation token to the ParallelOptions.CancellationToken property. Even though the cancellation happens too late to stop Parallel.ForEach() from starting all of the actions, by providing the token in the options, it can recognize that the exception thrown by each action was caused by the same cancellation token used in the options. With that, it then can throw just a single OperationCanceledException, instead of wrapping all of the action exceptions in an AggregateException.
I am assuming you are not actually
In response to
My question is why takes more than 20 sec to cancel the operation (parallel for) if the CancelationTokenSource is set to finish in 10 sec
This happens because you are not cancelling the Parallel.ForEach
In order to actually cancel you need to to use
po.CancellationToken.ThrowIfCancellationRequested();
inside the Parallel.ForEach code
As previous answer pointed out, if you want to actually cancel the task created by Task.Delay() you need to use the overload of Task.Delay which accepts a CancellationToken
Task.Delay(i * 1000, po.CancellationToken).Wait();
public static Task Delay(
TimeSpan delay,
CancellationToken cancellationToken
)
More details here
MSDN How to: Cancel a Parallel.For or ForEach Loop

using await Task.Delay in a for kills performance

Let's say I want to start roughly N tasks per second distributed equally.
So I tried this:
public async Task Generate(int numberOfCallsPerSecond)
{
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
Task t = Call(); // don't wait for result here
await Task.Delay(delay);
}
}
At first I expected this to run in 1 second but for numberOfCallsPerSecond = 100 it takes 16 seconds on my 12 core CPU.
It seems the await Task.Delay adds a lot of overhead (of course without it in place generation of the calls happens in 3ms.
I didn't expect that await would add so much overhead in this scenario. Is this normal?
EDIT:
Please forget about the Call(). Running this code shows similiar result:
public async Task Generate(int numberOfCallsPerSecond)
{
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
await Task.Delay(delay);
}
}
I tried to run it with numberOfCallsPerSecond = 500 and it takes around 10 seconds, I expected Generate to take roughly 1 second, not 10 times more
Task.Delay is lightweight but not accurate. Since the loop without delay completes much faster, it sounds like your thread is going idle and using an OS sleep to wait for the timer to elapse. The timer is checked according to the OS thread scheduling quantum (in the same interrupt handler which performs thread pre-emption), which is 16ms by default.
You can reduce the quantum with timeBeginPeriod, but a better (more power efficient) approach if you need rate limiting rather than exact timing is to keep track of elapsed time (the Stopwatch class is good for this) and number of calls made, and only delay when calls made have caught up to elapsed time. The overall effect is that your thread will wake up ~60 times per second, and start a few work items each time it does. If your CPU becomes busy with something else, you'll start extra work items when you get control back -- although it's also pretty straightforward to cap the number of items started at once, if that's what you need.
public async Task Generate(int numberOfCallsPerSecond)
{
var elapsed = Stopwatch.StartNew();
var delay = TimeSpan.FromMilliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond milliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
Call(); // don't wait for result here
int expectedI = elapsed.Elapsed.TotalSeconds * numberOfCallsPerSecond;
if (i > expectedI) await Task.Delay(delay);
}
}
My psychic debugger says your Call method has a significant synchronous part (i.e the part before an await) which takes time to execute synchronously.
If you want the Generate method only to "fire up" these Call calls and have them run concurrently (including the synchronous parts) you need to offload them to a ThreadPool thread using Task.Run:
var task = Task.Run(() => Call());
await Task.Delay(delay);
Task.Delay adds almost no overhead. It uses a System.Threading.Timer internally that requires very little resources.
If you use a timespan with Task.Delay(), it'll kill the CPU. Use an integer and it wont. True story. no idea why.

Best way to schedule deferred execution of method using ThreadPool?

I have a server application which needs to schedule the deferred execution of method(s). In other words, mechanism to run a method using a thread in ThreadPool after a certain period of time.
void ScheduleExecution (int delay, Action someMethod){
//How to implement this???
}
//At some other place
//MethodX will be executed on a thread in ThreadPool after 5 seconds
ScheduleExecution (5000, MethodX);
Please suggest an efficient mechanism to achieve above. I would prefer to avoid frequently creating new objects since above activity is likely to happen A LOT on server. Also the accuracy of call is important, i.e. while MethodX being executed after 5200 msec is fine but being executed after 6000 msec is a problem.
Thanks in advance...
You could use the RegisterWaitForSingleObject method. Here's an example:
public class Program
{
static void Main()
{
var waitHandle = new AutoResetEvent(false);
ThreadPool.RegisterWaitForSingleObject(
waitHandle,
// Method to execute
(state, timeout) =>
{
Console.WriteLine("Hello World");
},
// optional state object to pass to the method
null,
// Execute the method after 2 seconds
TimeSpan.FromSeconds(2),
// Execute the method only once. You can set this to false
// to execute it repeatedly every 2 seconds
true);
Console.ReadLine();
}
}

Categories

Resources