I want to delay 80 milliseconds between task 1 and task 2.
But here Task 1 and Task 2 run together.
my code:
private CancellationTokenSource cts = new CancellationTokenSource();
private async void button4_Click(object sender, EventArgs e)
{
//SendBuyOrder();
try
{
await Task.WhenAll(Task1(cts.Token), Task2(cts.Token));
//await Task.WhenAll(Task1(cts.Token));
}
catch (Exception ex)
{
}
}
public void send(int t)
{
txtResult.AppendText("Task" + t + ": " + DateTime.Now.ToString("hh:mm:ss.fff"));
txtResult.AppendText(Environment.NewLine);
txtResult.AppendText(Environment.NewLine);
}
public async Task Task1(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(Convert.ToInt32(txtRepeatInterval.Text), token);
send(1);
}
}
public async Task Task2(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(Convert.ToInt32(txtRepeatInterval.Text), token);
send(2);
}
}
Here, Task 1 and Task 2 are run together, and 80 milliseconds after Task 1 is run again. I want task 2 to be performed 80 milliseconds after starting task 1
enter image description here
You can make one task that does both:
public async Task Task3(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(Convert.ToInt32(txtRepeatInterval.Text), token);
send(1);
await Task.Delay(80, token);
send(2);
}
}
Related
We have below class members for timer:
private Timer _activityTimer;
Instantiating this timer variable in one method:
_activityTimer =
new Timer(async (timerState) => await UpdateActivityAsync(_ipAddress), null, new Random().Next(1, 15000), 15000);
But this did not calling periodically when server has load.
It is showing below log in serilog:
Starting HttpMessageHandler cleanup cycle with {InitialCount} items
Ending HttpMessageHandler cleanup cycle after {ElapsedMilliseconds}ms - processed: {DisposedCount} items - remaining: {RemainingItems} items
To handle async periodic callback, I would use System.Threading.PeriodicTimer. This way the execution of the next UpdateActivityAsync will not begin until the last one is done. If you still face thread pool starvation issue, you could manually create an additional thread in which you run the timer.
class Example {
private PeriodicTimer _activityTimer;
private IPAddress _ipAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void StopTimer() {
_activityTimer.Dispose();
}
private async Task StartTimerLoopAsync() {
_activityTimer = new PeriodicTimer(TimeSpan.FromMilliseconds(15000));
while (await _activityTimer.WaitForNextTickAsync()) {
await UpdateActivityAsync(_ipAddress);
}
}
private async Task UpdateActivityAsync(IPAddress ipAddress) {
await Task.Delay(500); // Simulate some IO
Console.WriteLine(ipAddress);
}
}
UPDATE for .NET Core 3.1
Instead of PeriodicTimer you could simply use Task.Delay (it's not very accurate, but good enough for your use case I believe):
class Example {
private CancellationTokenSource _cancellationTokenSource = new ();
private IPAddress _ipAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void StopTimer() {
_cancellationTokenSource.Cancel();
}
private async Task StartTimerLoopAsync() {
await Task.Delay(new Random().Next(1, 15000)); // System.Threading.Timer first delay
while (!_cancellationTokenSource.IsCancellationRequested) {
// run the Delay and UpdateActivityAsync simultaneously and wait for both
var delayTask = Task.Delay(15000, _cancellationTokenSource.Token);
await UpdateActivityAsync(_ipAddress, _cancellationTokenSource.Token);
await delayTask;
}
}
private async Task UpdateActivityAsync(IPAddress ipAddress, CancellationToken cancellationToken) {
await Task.Delay(1500, cancellationToken); // Simulate some IO
Console.WriteLine(ipAddress);
}
}
UPDATE
You could also create your own async Timer:
class Example : IDisposable {
private AsyncTimer<Example>? _timer;
public IPAddress IpAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
if (_timer is not null) {
return;
}
_timer = new AsyncTimer<Example>(async (state, ct) => await UpdateActivityAsync(state.IpAddress, ct), this, new Random().Next(1, 15000), 15000);
}
public void StopTimer() {
_timer?.Stop();
}
private async Task UpdateActivityAsync(IPAddress ipAddress, CancellationToken cancellationToken) {
await Task.Delay(500, cancellationToken); // Simulate some IO
Console.WriteLine(ipAddress);
}
public void Dispose() {
_timer?.Dispose();
}
}
class AsyncTimer<T> : IDisposable {
public delegate Task AsyncTimerDelegate(T state, CancellationToken cancellationToken);
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly AsyncTimerDelegate _timerCallback;
private readonly TimeSpan _dueTime;
private readonly TimeSpan _interval;
private readonly T _state;
public AsyncTimer(AsyncTimerDelegate timerCallback, T state, int dueTime, int interval) {
_timerCallback = timerCallback;
_state = state;
_dueTime = TimeSpan.FromMilliseconds(dueTime);
_interval = TimeSpan.FromMilliseconds(interval);
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void Stop() {
_cancellationTokenSource.Cancel();
}
private async Task StartTimerLoopAsync() {
await Task.Delay(_dueTime);
while (!_cancellationTokenSource.IsCancellationRequested) {
// run the Delay and UpdateActivityAsync simultaneously and wait for both
var delayTask = Task.Delay(_interval, _cancellationTokenSource.Token);
await _timerCallback.Invoke(_state, _cancellationTokenSource.Token);
await delayTask;
}
}
public void Dispose() {
_cancellationTokenSource.Dispose();
}
}
I'd want to guarantee the order of execution in a single backgroundworker thread like this
One BackgroundWorker DoWork =
{
First();
Second();
Third();
...
}
Function First()
{
try{
X();
Y();
Z(); //etc
}catch
}
The question is marked for C#. Assuming the code is a representation of what you want to achieve in C# consider:
private void button1_Click(object sender, EventArgs e)
{
//If you need a return result
//Task.FromResult(MainThread().ConfigureAwait(false));
Task.Run(MainThread);
}
private async Task MainThread()
{
var result1 = await Task1().ConfigureAwait(false);
await Task2().ConfigureAwait(false);
var result3 = await Task3().ConfigureAwait(false);
}
private async Task<int> Task1()
{
await Task.Delay(2000).ConfigureAwait(false);
Debug.WriteLine("Executed task 1");
return 1;
}
private async Task Task2()
{
await Task.Delay(100).ConfigureAwait(false);
Debug.WriteLine("Executed task 2");
}
private async Task<int> Task3()
{
await Task.Delay(1000).ConfigureAwait(false);
Debug.WriteLine("Executed task 3");
return 3;
}
I want to add ability to cancel long running external method (in code below is it LongOperation method from SomeExternalClass class), so I wrap this method into task and add custom task extension for handle timeout.
TimeoutException is successful throwed after specific period of time, but external method is not canceled. How I can cancel LongOperation?
class Program
{
private static CancellationTokenSource cancellationTokenSource;
static async Task Main(string[] args)
{
cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.Token.ThrowIfCancellationRequested();
try
{
await SearchTask().WithTimeout(TimeSpan.FromSeconds(2));
}
catch (TimeoutException)
{
Console.WriteLine("Timeout");
cancellationTokenSource.Cancel();
}
Console.WriteLine("Program end");
Console.ReadKey();
}
private static Task SearchTask()
{
return Task.Run(() =>
{
Console.WriteLine("Start task");
SomeExternalClass.LongOperation();
Console.WriteLine("End task");
}, cancellationTokenSource.Token);
}
}
public static class SomeExternalClass
{
// this is simulation of long running 3rd party method
public static void LongOperation()
{
Console.WriteLine("Start LongOperation");
Thread.Sleep(10000);
Console.WriteLine("End LongOperation");
}
}
public static class TaskExtension
{
public static async Task WithTimeout(this Task task, TimeSpan timeout)
{
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
{
await task;
}
throw new TimeoutException();
}
}
The output is:
Start task
Start LongOperation
Timeout
Program end
End LongOperation
End task
I am using an asynchronous method. How can I stop its execution when a Timer raises a timeout event?
My code:
public async Task<object> Method()
{
cts = new CancellationTokenSource();
try
{
timer = new System.Timers.Timer(3000);
timer.Start();
timer.Elapsed += (sender, e) =>
{
try
{
timer_Elapsed(sender, e, cts.Token, thread);
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
return;
}
};
await methodAsync(cts.Token);
return "message";
}
catch (OperationCanceledException)
{
return "cancelled";
}
catch (Exception ex)
{
return ex.Message;
}
}
// Async Call
public async Task<object> methodAsync(CancellationToken ct)
{
try
{
pdfDocument = htmlConverter.Convert("path", "");
}
catch(Exception ex)
{
return x.Message;
}
}
// Timer event
void timer_Elapsed(object sender, ElapsedEventArgs e, CancellationToken ct)
{
cts.Cancel();
ct.ThrowIfCancellationRequested();
}
Here's how canceling a task works:
public async Task<object> Method()
{
cts = new CancellationTokenSource();
await methodAsync(cts.Token);
return "message";
}
public Task<object> methodAsync(CancellationToken ct)
{
for (var i = 0; i < 1000000; i++)
{
if (ct.IsCancellationRequested)
{
break;
}
//Do a small part of the overall task based on `i`
}
return result;
}
You have to respond to the change in the property of ct.IsCancellationRequested to know when to cancel the task. There is no safe way for one thread/task to cancel another thread/task.
In your case it appears that you are trying to call a single method that doesn't know about the CancellationToken so you can not cancel this task safely. You must let the thread/task continue to completion.
I think you can try mentioning when to cancel it. Something like
cts.CancelAfter(TimeSpan.FromMilliseconds(5000));
Also, you need to use the cancellation token in the called methods. That's when you will know when to cancel.
I'm trying to cancel the execution of the DoSomethingAsync method which I call using await.
When I click on the cancel button the execution is not cancelled and I don't see the "Execution was cancelled" message box, but instead I see the other message box.
I don't understand why it's not working. I am still learning this part of C# and I took this example at http://www.codeproject.com/Articles/127291/C-vNext-New-Asynchronous-Pattern#heading0015 (I simplified it).
public class MyClass : Class
{
CancellationTokenSource cts;
private async void searchButton_Click(object sender, EventArgs e)
{
await DoSomethingAsync();
}
private void cancelButton_Click(object sender, EventArgs e)
{
cts.Cancel();
}
async void DoSomethingAsync()
{
cts = new CancellationTokenSource();
try
{
await SuperSlowProcess();
MessageBox.Show("You will only see this if execution is not cancelled");
}
catch (TaskCanceledException)
{
MessageBox.Show("Execution was cancelled");
}
}
}
In order to make it working, you actually need to use CancellationToken in the SuperSlowProcess:
public Task SuperSlowProcess(CancellationToken cancellationToken)
{
return Task.Run(() => {
// you need to check cancellationToken periodically to check if cancellation has been requested
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested(); // this will throw OperationCancelledException after CancellationTokenSource.Cancel() is called
Thread.Sleep(200); // to emulate super slow process
}
});
}
Of course, it depends on the implementation of SuperSlowProcess. If it's not possible to check CancellationToken periodically, you could check it only once - in the very end, something like that:
public async Task SuperSlowProcess2(CancellationToken cancellationToken)
{
var response = await CallExternalApi();
cancellationToken.ThrowIfCancellationRequested();
}
and then
async void DoSomethingAsync()
{
cts = new CancellationTokenSource();
try
{
await SuperSlowProcess(cts.Token);
MessageBox.Show("You will only see this if execution is not cancelled");
}
catch (OperationCanceledException) // Note that exception type is different
{
MessageBox.Show("Execution was cancelled");
}
}