Reputation: 11151
I have a C# app that must run blocks of code in parallel. Here is the basic structure of two of those blocks of code. In reality, there will be many more.
private async Task MyFirstTask()
{
// do stuff
await FirstTaskImplementation();
// cleanup
}
private async Task MySecondTask()
{
// do stuff
await SecondTaskImplementation();
// cleanup
}
Some of these blocks of code will run on a timer. Some will run repeatedly. In an attempt to accomplish, I have the following:
Task.Run(() => MyFirstTask());
Task.Run(() => MySecondTask());
When MyFirstTask
has completed, I want to run it again. In fact, I want to run it over-and-over again until the program stops. Yet, I want MySecondTask
to run in parallel of MyFirstTask
. My question is, how do I execute MyFirstTask
repeatedly, while still being parallel to MySecondTask
?
I reviewed several of the related SO questions. I also do not see a Complete
kind of event handler. So, I'm kind of lost in terms of how to implement this. I appreciate your help!
Upvotes: 1
Views: 921
Reputation: 3697
Another approach would be something like this:
var first=MyFirstTask().ToObservable();
var second=MySecondTask().ToObservable();
first.Repeat().Merge(second).Subscribe(x=>Console.WriteLine("Task completing."));
That's illustrative, not tested code. If you expect MySecondTask to complete, then perhaps this:
first.Repeat().TakeUntil(second).Subscribe(x=>Console.WriteLine("Task completing."));
If you want to add timeouts to second you could do this:
first.Repeat().TakeUntil(second.Timeout(TimeSpan.FromMilliseconds(100))).Subscribe(...)
If you want to show something on each task completion, declare the observables as:
var first=MyFirstTask().ToObservable().Do(Console.WriteLine("First completing"));
The above requires System.Reactive.Linq namespaces. I find these Rx based solutions to concurrency generally more elegant than the TPL, but that's just subjective.
Finally, if you do not want to start the tasks until the subscription is called, or something is ready to start watching, you can use Observable.FromAsync , as per info here
Upvotes: 0
Reputation: 25623
The beauty of async/await
is that you can write asynchronous code in much the same way you'd write synchronous code. How would you repeat a synchronous operation? You could use a loop. You could do the same here, e.g.:
private async Task MyFirstTask(CancellationToken token) {
while (!token.IsCancellationRequested) {
// do stuff
await FirstTaskImplementation();
// cleanup
}
}
You can embed the loop in your current method or lift it into a wrapper method, but it should work either way.
You can continue scheduling your tasks the same way you're doing it now, though you really should await
your async
methods:
CancellationTokenSource cts = new CancellationTokenSource();
Task.Run(async () => await MyFirstTask(cts.Token));
Task.Run(async () => await MySecondTask());
// Request cancellation via `cts` when you want the looping to end.
And although you didn't ask about it, if you wanted to insert a delay between each iteration, you could simply place an await Task.Delay(...)
statement at the end of the loop body.
Upvotes: 1
Reputation: 41018
You don't necessarily need to use Task.Run
for the first task. You can use a Timer for the first task, with AutoReset
set to true
. That way it'll run forever and you don't have to worry about it anymore.
private static System.Timers.Timer aTimer;
public static void Main()
{
SetTimer(); //MyFirstTask starts running over and over in another thread
Task.Run(() => MySecondTask());
}
private static void SetTimer()
{
// Create a timer with a 1ms interval (has to be > 0)
aTimer = new System.Timers.Timer(1);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += MyFirstTask;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private static async void MyFirstTask(Object source, ElapsedEventArgs e)
{
// do stuff
await FirstTaskImplementation();
// cleanup
}
private async Task MySecondTask()
{
// do stuff
await SecondTaskImplementation();
// cleanup
}
Upvotes: 0