This may be an odd question and it is really for my educational purpose so I can apply it in future scenarios that may come up.
I am using C#.
I am stress testing so this is not quite production code.
I upload data to my server via a web service.
I start the service off using a Task.Run.
I check to see if the Task is completed before allowing the next Run.Task to begin.
This is done within a loop.
However, because I am using a modular declared Task will not the result be affected?
I could declare a local Task.Run variable but I want to see how far I can get with this question 1st.
If the Task.Run can raise an event to say it is completed then this may not be an issue?
This is my code:
//module declaration:
private static Task webTask = Task.Run(() => { System.Windows.Forms.Application.DoEvents(); });
//in a function called via a timer
if (webTask.IsCompleted)
{
//keep count of completed tasks
}
webTask = Task.Run(() =>
{
try
{
wcf.UploadMotionDynamicRaw(bytes); //my web service
}
catch (Exception ex)
{
//deal with error
}
);
IMO you do not need the timer. Using Task Continuation you subscribe to the done event:
System.Threading.Tasks.Task
.Run(() =>
{
// simulate processing
for (var i = 0; i < 10; i++)
{
Console.WriteLine("do something {0}", i + 1);
}
})
.ContinueWith(t => Console.WriteLine("done."));
The output is:
do something 1
do something 2
.
.
do something 9
do something 10
done
Your code could look like this:
var webTask = Task.Run(() =>
{
try
{
wcf.UploadMotionDynamicRaw(bytes); //my web service
}
catch (Exception ex)
{
//deal with error
}
}).ContinueWith(t => taskCounter++);
With task continuation you could even differentiate between failed and success process result, if you want to count only successfull tasks - using the TaskContinuationOptrions.
You can wait for your task to complete by awaiting your task like this
await webTask;
that will asynchronously wait for 'webTask' to complete. Instead of the timer you can use await Task.Delay which will asynchronously wait for the delay to expire. I would also consider making the wcf call asynchronous so you don't have to call inside Task.Run. See this question for some tips.
I'd rewrite the code as follows:
public async Task UploadAsync()
{
while(true)
{
await Task.Delay(1000); // this is essentially your timer
// wait for the webTask to complete asynchrnously
await webTask;
//keep count of competed tasks
webTask = Task.Run(() =>
{
try
{
// consider generating an asynchronous method for this if possible.
wcf.UploadMotionDynamicRaw(bytes); //my web service
}
catch (Exception ex)
{
//deal with error
}
});
}
}
Related
I'm calling two functions that rely on some external web services. For now, they run in parallel and when they both complete the execution resumes. However, if the external servers take too much time to process the requests, it could lock my code for a while.
I want to add a timeout so that if the servers take more than 10 seconds to respond then just continue on. This is what I have, how can I add a timeout?
Parallel.Invoke(
() => FunctionThatCallsServer1(TheParameter),
() => FunctionThatCallsServer2(TheParameter)
);
RunThisFunctionNoMatterWhatAfter10Seconds();
I don't think there's an easy way of timing out a Parallel.Invoke once the functions have started, which clearly they will have done after ten seconds here. Parallel.Invoke waits for the functions to complete even if you cancel, so you would have to find a way to complete the functions early.
However, under the covers Parallel.Invoke uses Tasks, and if you use Tasks directly instead of Parallel.Invoke then you can provide a timeout. The code below shows how:
Task task1 = Task.Run(() => FunctionThatCallsServer1(TheParameter));
Task task2 = Task.Run(() => FunctionThatCallsServer2(TheParameter));
// 10000 is timeout in ms, allTasksCompleted is true if they completed, false if timed out
bool allTasksCompleted = Task.WaitAll(new[] { task1, task2 }, 10000);
RunThisFunctionNoMatterWhatAfter10Seconds();
One slight difference this code has with Parallel.Invoke is that if you have a VERY large number of functions then Parallel.Invoke will manage the Task creation better than just blindly creating a Task for every function as here. Parallel.Invoke will create a limited number of Tasks and re-use them as the functions complete. This won't be an issue with just a few functions to call as above.
You will need to create an instance of CancellationTokenSource and right at creating time you ca configure your timeout time, like
var cts = new CancellationTokenSource(timeout);
then you will need to create an instance of ParallelOptions where you set the ParallelOptions.CancellationToken to the token of the CancellationTokenSource, like
var options = new ParallelOptions {
CancellationToken = cts.Token,
};
Then you can call Parallel.Invoke with the options and your actions
try
{
Parallel.Invoke(
options,
() => FunctionThatCallsServer1(token),
() => FunctionThatCallsServer2(token)
);
}
catch (OperationCanceledException ex)
{
// timeout reached
Console.WriteLine("Timeout");
throw;
}
but you will also need to hand the token to the called Server functions and handle the timeout in these actions aswell.
This is because the Parallel.Invoke will only check before it starts an action if the token it got is cancelled. That means if all actions are started before the timeout occures the Parallel.Invoke call will block as long as the actions need to finish.
Update:
A good way to test the cancellation is to define FunctionThatCallsServer1 like,
static void FunctionThatCallsServer1(CancellationToken token) {
var endTime = DateTime.Now.AddSeconds(5);
while (DateTime.Now < endTime) {
token.ThrowIfCancellationRequested();
Thread.Sleep(1);
}
}
Below is the code:
using System;
using System.Threading.Tasks;
namespace Algorithums
{
public class Program
{
public static void Main(string[] args)
{
ParelleTasks();
Console.WriteLine("Main");
Console.ReadLine();
}
private static void ParelleTasks()
{
Task t = Task.Run(() => {
FunctionThatCallsServers();
Console.WriteLine("Task ended after 20 Seconds");
});
try
{
Console.WriteLine("About to wait for 10 sec completion of task {0}", t.Id);
bool result = t.Wait(10000);
Console.WriteLine("Wait completed normally: {0}", result);
Console.WriteLine("The task status: {0:G}", t.Status);
}
catch (OperationCanceledException e)
{
Console.WriteLine("Error: " + e.ToString());
}
RunThisFunctionNoMatterWhatAfter10Seconds();
}
private static bool FunctionThatCallsServers()
{
Parallel.Invoke(
() => FunctionThatCallsServer1(),
() => FunctionThatCallsServer2()
);
return true;
}
private static void FunctionThatCallsServer1()
{
System.Threading.Thread.Sleep(20000);
Console.WriteLine("FunctionThatCallsServer1");
}
private static void FunctionThatCallsServer2()
{
System.Threading.Thread.Sleep(20000);
Console.WriteLine("FunctionThatCallsServer2");
}
private static void RunThisFunctionNoMatterWhatAfter10Seconds()
{
Console.WriteLine("RunThisFunctionNoMatterWhatAfter10Seconds");
}
}
}
I have a console program which sends async HTTP requests to an external web API. (HttpClient.GetAsync());)
These tasks can take several minutes to complete - during which I'd like to be able to show to the user that the app is still running - for example by sending Console.WriteLine("I ain't dead - yet") every 10 seconds.
I am not sure how to do it right, without the risk of hiding exceptions, introducing deadlocks etc.
I am aware of the IProgress<T>, however I don't know whether I can introduce it in this case. I am await a single async call which does not report progress. (It's essentially an SDK which calls httpClient GetAsync() method
Also:
I cannot set the GUI to 'InProgress', because there is no GUI, its a console app - and it seems to the user as if it stopped working if I don't send an update message every now and then.
Current idea:
try
{
var task = httpClient.GetAsync(uri); //actually this is an SDK method call (which I cannot control and which does not report progress itself)
while (!task.IsCompleted)
{
await Task.Delay(1000 * 10);
this.Logger.Log(Verbosity.Verbose, "Waiting for reply...");
}
onSuccessCallback(task.Result);
}
catch (Exception ex)
{
if (onErrorCallback == null)
{
throw this.Logger.Error(this.GetProperException(ex, caller));
}
this.Logger.Log(Verbosity.Error, $"An error when executing command [{action?.Command}] on {typeof(T).Name}", ex);
onErrorCallback(this.GetProperException(ex, caller));
}
Let me tidy this code up a bit for you
async Task Main()
{
var reporter = new ConsoleProgress();
var result = await WeatherWaxProgressWrapper(() => GetAsync("foo"), reporter);
Console.WriteLine(result);
}
public async Task<int> GetAsync(string uri)
{
await Task.Delay(TimeSpan.FromSeconds(10));
return 1;
}
public async Task<T> WeatherWaxProgressWrapper<T>(Func<Task<T>> method, System.IProgress<string> progress)
{
var task = method();
while(!task.IsCompleted && !task.IsCanceled && !task.IsFaulted)
{
await Task.WhenAny(task, Task.Delay(1000));
progress.Report("I ain't dead");
}
return await task;
}
public class ConsoleProgress : System.IProgress<string>
{
public void Report(string value)
{
Console.WriteLine(value);
}
}
You could have a never-ending Task as a beacon that signals every 10 sec, and cancel it after the completion of the long running I/O operation:
var beaconCts = new CancellationTokenSource();
var beaconTask = Task.Run(async () =>
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(10), beaconCts.Token);
Console.WriteLine("Still going...");
}
});
await LongRunningOperationAsync();
beaconCts.Cancel();
You are looking for System.Progress<T>, a wonderful implementation of IProgress.
https://learn.microsoft.com/en-us/dotnet/api/system.progress-1
You create an object of this class on the "UI thread" or the main thread in your case, and it captures the SynchronizationContext for you. Pass it to your worker thread and every call to Report will be executed on the captured thread, you don't have to worry about anything.
Very useful in WPF or WinForms applications.
I'm still getting up to speed with async & multi threading. I'm trying to monitor when the Task I Start is still running (to show in a UI). However it's indicating that it is RanToCompletion earlier than I want, when it hits an await, even when I consider its Status as still Running.
Here is the sample I'm doing. It all seems to be centred around the await's. When it hits an await, it is then marked as RanToCompletion.
I want to keep track of the main Task which starts it all, in a way which indicates to me that it is still running all the way to the end and only RanToCompletion when it is all done, including the repo call and the WhenAll.
How can I change this to get the feedback I want about the tskProdSeeding task status?
My Console application Main method calls this:
Task tskProdSeeding;
tskProdSeeding = Task.Factory.StartNew(SeedingProd, _cts.Token);
Which the runs this:
private async void SeedingProd(object state)
{
var token = (CancellationToken)state;
while (!token.IsCancellationRequested)
{
int totalSeeded = 0;
var codesToSeed = await _myRepository.All().ToListAsync(token);
await Task.WhenAll(Task.Run(async () =>
{
foreach (var code in codesToSeed)
{
if (!token.IsCancellationRequested)
{
try
{
int seedCountByCode = await _myManager.SeedDataFromLive(code);
totalSeeded += seedCountByCode;
}
catch (Exception ex)
{
_logger.InfoFormat(ex.ToString());
}
}
}
}, token));
Thread.Sleep(30000);
}
}
If you use async void the outer task can't tell when the task is finished, you need to use async Task instead.
Second, once you do switch to async Task, Task.Factory.StartNew can't handle functions that return a Task, you need to switch to Task.Run(
tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);
Once you do both of those changes you will be able to await or do a .Wait() on tskProdSeeding and it will properly wait till all the work is done before continuing.
Please read "Async/Await - Best Practices in Asynchronous Programming" to learn more about not doing async void.
Please read "StartNew is Dangerous" to learn more about why you should not be using StartNew the way you are using it.
P.S. In SeedingProd you should switch it to use await Task.Delay(30000); insetad of Thread.Sleep(30000);, you will then not tie up a thread while it waits. If you do this you likely could drop the
tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);
and just make it
tskProdSeeding = SeedingProd(_cts.Token);
because the function no-longer has a blocking call inside of it.
I'm not convinced that you need a second thread (Task.Run or StartNew) at all. It looks like the bulk of the work is I/O-bound and if you're doing it asynchronously and using Task.Delay instead of Thread.Sleep, then there is no thread consumed by those operations and your UI shouldn't freeze. The first thing anyone new to async needs to understand is that it's not the same thing as multithreading. The latter is all about consuming more threads, the former is all about consuming fewer. Focus on eliminating the blocking and you shouldn't need a second thread.
As others have noted, SeedingProd needs to return a Task, not void, so you can observe its completion. I believe your method can be reduced to this:
private async Task SeedingProd(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
int totalSeeded = 0;
var codesToSeed = await _myRepository.All().ToListAsync(token);
foreach (var code in codesToSeed)
{
if (token.IsCancellationRequested)
return;
try
{
int seedCountByCode = await _myManager.SeedDataFromLive(code);
totalSeeded += seedCountByCode;
}
catch (Exception ex)
{
_logger.InfoFormat(ex.ToString());
}
}
await Task.Dealy(30000);
}
}
Then simply call the method, without awaiting it, and you'll have your task.
Task mainTask = SeedingProd(token);
When you specify async on a method, it compiles into a state machine with a Task, so SeedingProd does not run synchronously, but acts as a Task even if returns void. So when you call Task.Factory.StartNew(SeedingProd) you start a task that kick off another task - that's why the first one finishes immediately before the second one. All you have to do is add the Task return parameter instead of void:
private async Task SeedingProdAsync(CancellationToken ct)
{
...
}
and call it as simply as this:
Task tskProdSeeding = SeedingProdAsync(_cts.Token);
I have the following code
var exceptions = new ConcurrentQueue<Exception>();
Task task = Task.Factory.StartNew(() =>
{
try
{
Parallel.Invoke(
async () => await _aViewModel.LoadData(_someId),
async () => await _bViewModel.LoadData(_someId)
);
}
catch (Exception ex)
{
exceptions.Enqueue(ex);
}
}).ContinueWith((continuation) =>
{
if (exceptions.Count > 0) throw new AggregateException(exceptions);
});
I am using Task.StartNew here because the LoadData method use the Dispatcher.StartAsync method to invoke on the main UI thread internally.
The problem I have is that if I force _aViewModel.LoadData to throw an exception it is not caught in the Catch(Exception) clause (nor if I catch AggregateException). I don't understand why!?
Parallel.Invoke is not async-aware. So your async lambdas are being converted to async void methods, which have extremely awkward error semantics (they are not allowed to leave the async void method; instead, they are captured and re-raised directly on the SynchronizationContext that was active at the time the async void method started - in this case, the thread pool).
I'm not sure why you have the Parallel.Invoke in the first place. Since your method is already async, you could just do something like this:
Task task = Task.Factory.StartNew(async () =>
{
try
{
Task.WaitAll(
_aViewModel.LoadData(_someId),
_bViewModel.LoadData(_someId)
);
}
catch (Exception ex)
{
exceptions.Enqueue(ex);
}
})...
P.S. If you have the time, rethink the structure of this whole part of the code. Dispatcher.StartAsync is a code smell. The UI should be (asynchronously) requesting data; the data retrieval objects should not have to know about the UI.
Parallel.Invoke takes an array of Action delegates. It has no means of knowing that your delegates are actually async methods, and therefore it returns before your tasks have completed.
For an in-depth explanation of this behaviour, watch Lucian Wischik's Channel 9 video on the subject.
Try changing your code to use the Task.WhenAll method instead.
var aTask = _aViewModel.LoadData(_someId);
var bTask = _bViewModel.LoadData(_someId);
await Task.WhenAll(aTask, bTask);
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.