Here's some code I'm working with, that fires of three tasks. My Stop is called, and from the output, it looks as though the tasks finish after Stop returns, which isn't what I'd expect.
This is Visual Studio 2013 with .NET 4.5.1
I tried the same using AutoResetEvent which worked as expected.
Here's the output from this:
Three tasks without auto reset
Starting...
FIRST
SECOND
THIRD
Running...
FIRST
FIRST
FIRST
SECOND
FIRST
FIRST
THIRD
...
SECOND
FIRST
Stopping...
THIRD
All done!
First done
Second done
Third done
What I'd expect to see is:
First done
Second done
Third done
All done!
Here's the code (I even added locking around the Console output, but it made no difference):
public class ThreeTasksWithoutAutoResetEvent
{
private CancellationTokenSource cancellation;
private Task[] tasks;
private object obj = new object();
public ThreeTasksWithoutAutoResetEvent()
{
Console.WriteLine("Three tasks without auto reset");
cancellation = new CancellationTokenSource();
tasks = new Task[3];
Message("Starting...");
Start();
Message("Running...");
Thread.Sleep(3000);
Message("Stopping...");
Stop();
Message("All done!");
}
private void Start()
{
tasks[0] = this.First(cancellation.Token);
tasks[1] = this.Second(cancellation.Token);
tasks[2] = this.Third(cancellation.Token);
}
private async void Stop()
{
cancellation.Cancel();
await Task.WhenAll(tasks);
}
private async Task First(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("FIRST");
Thread.Sleep(100);
}
Message("First done");
}, token);
}
private async Task Second(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("SECOND");
Thread.Sleep(300);
}
Message("Second done");
}, token);
}
private async Task Third(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("THIRD");
Thread.Sleep(500);
}
Message("Third done");
}, token);
}
private void Message(string message)
{
lock (obj)
{
Console.WriteLine(message);
}
}
}
Because you're not waiting for Stop method to finish. You can't wait for an async void method. You need return a Task, so that the caller can wait/await for it to complete.
private Task Stop()
{
cancellation.Cancel();
return Task.WhenAll(tasks);
}
Then you can call Stop().Wait(); instead of Stop. That will wait for WhenAll to complete.
Related
The structure of my code is shown below:
internal async Task ButtonClickMethod()
{
await this.InitializeMethod();
}
internal async Task InitializeMethod()
{
await this.Call();
}
private async Task Call()
{
await this.CallTasks();
}
private async Task CallTasks()
{
List<Task> tasks = new List<Task>();
tasks.Add(/*time-consuming task A*/);
tasks.Add(/*time-consuming task B*/);
tasks.Add(/*time-consuming task C*/);
tasks.Add(/*time-consuming task D*/);
await Task.WhenAll(tasks);
}
Task defined as follows:
/*time-consuming task*/
internal async Task TimeConsuming()
{
// code...
await Task.Run(() =>
{
// code... A/B/C/D
});
// code...
}
Is there any problem with this code that will cause the UI to freeze?
Is the problem caused by Task.WhenAll?
Use this instead :
private async void ClickButton(object sender, RoutedEventArgs e)
{
await ThatFirstMethod(any parameters);
}
private async Task ThatFirstMethod(any parameters)
{
await Task.Run(() => {
ThatFirstTimeConsumingMethod(any param);
});
await Task.Run(() => {
ThatSecondTimeConsumingMethod(any param);
});
}
private bool ThatFirstTimeConsumingMethod(any param)
{
//code
return true;
}
.....
If you have to access UI Elements you will not be able to do so, beacause your "heavy" methods are being processed by another Thread.
Here's a solution :
private bool ThatFirstTimeConsumingMethod(any param)
{
string s = "";
Application.Current.Dispatcher.Invoke(() => {
s = myTextBox.Text;
});
// do what you want with the s variable
Application.Current.Dispatcher.Invoke(() => {
myTextBox.Text = s;
});
return true;
}
So basically, you're asking your main Thread to execute an action (where your UI is getting processed) with Application.Current.Dispatcher.Invoke. And when you're in the await Task.Run you're asking another thread to process the heavy action.
I simple do not know how to make the following code work, it is suppose to keep running forever inside an infinite loop, and in fact it will work when I remove await Task.Delay from the method Method_FOO, but I need Method_FOO to be async.
I think to make this work the Thread.Start() method needs to be "waitable" (not just the code it runs), but Thread.Start is void. I notice that if I block the execution with eg.: Console.ReadLine it will print the Worked string, but this is not a solution, and is terrible in real life.
This code is just an example, but the threads need to run inside an infinite loop (it is not something I can change), and I need async methods because I need to consume some websockets, and looks like there is no sync client websocket class in C#.
But still, there must be a simple/decent solution for this problem.
public static class Program
{
public static async Task Main(string[] args)
{
var cancellationTokenSource = new CancellationTokenSource();
var thread1 = new Thread(async () => await Run(cancellationTokenSource.Token, "threadName1", Method_FOO));
var thread2 = new Thread(async () => await Run(cancellationTokenSource.Token, "threadName2", Method_FOO));
thread1.Start();
thread2.Start();
}
private static async Task Method_FOO(CancellationToken cancellationToken)
{
Console.WriteLine("It is called...");
await Task.Delay(300, cancellationToken);
//never reach this part
Console.WriteLine("Worked ...");
}
// workd but it is not async
//private static Task Method_FOO(CancellationToken cancellationToken)
//{
// Console.WriteLine("It is called...");
// Console.WriteLine("Worked ...");
// return Task.CompletedTask;
//}
private static async Task Run(CancellationToken cancellationToken, string threadName, Func<CancellationToken, Task> function)
{
try
{
while (true)
{
await function(cancellationToken);
Console.WriteLine($"{threadName} waiting ...");
cancellationToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
At this point in history, new Thread is pretty much only useful for COM interop. In every other scenario, there are better solutions. In this case, you can send work to the thread pool by using Task.Run:
public static async Task Main(string[] args)
{
var cancellationTokenSource = new CancellationTokenSource();
var task1 = Task.Run(async () => await Run(cancellationTokenSource.Token, "threadName1", Method_FOO));
var task2 = Task.Run(async () => await Run(cancellationTokenSource.Token, "threadName2", Method_FOO));
await Task.WhenAll(task1, task2);
}
Thread don't wait task's end. When the 3 threads (main, thread1, thread2), the program is closed and running task killed.
You can make the method Run synchronous and manualy wait the task end, like :
public static class Program
{
public static async Task Main(string[] args)
{
var cancellationTokenSource = new CancellationTokenSource();
var thread1 = new Thread(() => Run(cancellationTokenSource.Token, "threadName1", Method_FOO));
var thread2 = new Thread(() => Run(cancellationTokenSource.Token, "threadName2", Method_FOO));
thread1.Start();
thread2.Start();
}
private static async Task Method_FOO(CancellationToken cancellationToken)
{
Console.WriteLine("It is called...");
await Task.Delay(300, cancellationToken);
Console.WriteLine("Worked ...");
}
private static void Run(CancellationToken cancellationToken, string threadName, Func<CancellationToken, Task> function)
{
try
{
while (true)
{
function(cancellationToken).Wait();
Console.WriteLine($"{threadName} waiting ...");
cancellationToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
Edit : Why not directly use the tasks?
public static void Main(string[] args)
{
var cancellationTokenSource = new CancellationTokenSource();
var task1 = Run(cancellationTokenSource.Token, "threadName1", Method_FOO);
var task2 = Run(cancellationTokenSource.Token, "threadName2", Method_FOO);
Task.Delay(TimeSpan.FromSeconds(5)).ContinueWith(t => cancellationTokenSource.Cancel());
Task.WaitAll(task1, task2);
}
I've created a little demo project to help me understand how I can use Cancellation Tokens. I understand that you cancel a token and check if a cancellation has been requested, but is there a way that I can check whether the cancellation has been realised? In my example below, I don't want to run Work() again until DoWork() has finished running.
public class Program
{
public static CancellationTokenSource tokenSource;
private static void Main(string[] args)
{
while (true)
{
Work();
}
}
public static async void Work()
{
tokenSource = new CancellationTokenSource();
Console.WriteLine("Press any key to START doing work");
Console.ReadLine();
Console.WriteLine("Press any key to STOP doing work");
DoWork(tokenSource.Token);
Console.ReadLine();
Console.WriteLine("Stopping...");
tokenSource.Cancel();
}
public static async void DoWork(CancellationToken cancelToken)
{
while (true)
{
Console.WriteLine("Working...");
await Task.Run(() =>
{
Thread.Sleep(1500);
});
if (cancelToken.IsCancellationRequested)
{
Console.WriteLine("Work Cancelled!");
return;
}
}
}
}
You typically don't want to make your DoWork function async void -- make it async Task instead. That way you can see when it's completed (or been cancelled).
You also probably want to use cancelToken.ThrowIfCancellationRequested(). This throws an OperationCanceledException, which you can catch.
public class Program
{
public static CancellationTokenSource tokenSource;
private static async Task Main(string[] args)
{
while (true)
{
await Work();
}
}
public static async Task Work()
{
tokenSource = new CancellationTokenSource();
Console.WriteLine("Press any key to START doing work");
Console.ReadLine();
Console.WriteLine("Press any key to STOP doing work");
var task = DoWork(tokenSource.Token);
Console.ReadLine();
Console.WriteLine("Stopping...");
tokenSource.Cancel();
try
{
await task;
}
catch (OperationCanceledException)
{
// Task was cancelled
}
}
public static async Task DoWork(CancellationToken cancelToken)
{
while (true)
{
Console.WriteLine("Working...");
await Task.Run(() =>
{
Thread.Sleep(1500);
});
cancelToken.ThrowIfCancellationRequested();
}
}
}
This code relies on "async main", which was introduced in C# 7. If you don't have this, you can write your Main method as:
private static void Main(string[] args)
{
while (true)
{
Work().Wait();
}
}
You will make the most out of a CancellationToken if all your operations are asynchronous and cancellable. This way cancelling the token will have immediate effect. You won't have to wait for a Thread.Sleep or other blocking call to complete.
public static async Task DoWork(CancellationToken cancellationToken)
{
while (true)
{
await Console.Out.WriteLineAsync("Working...");
await Task.Delay(1500, cancellationToken);
}
}
In this example the token is passed only to Task.Delay, because the WriteLineAsync is not cancellable on .NET Framework (it is on .NET Core).
An OperationCanceledException will be raised by Task.Delay when the token is cancelled.
I have an event in my WPF .NET 4.5 application that can be triggered up to 10 times per second, the event is computing data that is not required that often so I'm looking for a way to remove unnecessary computation stress and call the method if event wasn't triggered for 3 seconds. I thought about using async functionality like that:
private async Task DoWork()
{
await Task.Delay(3000);
...
}
private void Event()
{
Task.Run(() => DoWork());
}
I'm not really sure about how to elegantly handle disposing of unwanted Tasks, ie. when user trigger the event, every task that was created by that event should be terminated and it should be as fast as possible. I've tried with CancellationToken but I'm not sure this is the right approach for my case
You can use CancellationTokenSource to let the code inside task know that it is canceled:
private CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource ();
Let's change:
private async Task DoWork()
{
await Task.Delay(3000);
...
}
To:
private async Task DoWork(int timeout = 3000)
{
await Task.Delay(timeout, CancellationTokenSource.Token);
if(!CancellationTokenSource.Token.IsCancellationRequested)
{
...
}
}
Now we can cancel our task if required:
CancellationTokenSource.Cancel();
Task.Delay will observe the CancellationToken and if it is in a Canceled state it will abort the task execution. Later in code we check whether we need to do anything or not.
That was my solution based on #Fabjan answer.
private static void RunSingleTask(ref CancellationTokenSource cts, int delay, Action func)
{
if (cts != null)
{
cts.Cancel();
cts.Dispose();
}
cts = new CancellationTokenSource();
var token = cts.Token;
Task.Run(async () =>
{
try
{
await Task.Delay(delay, token);
}
catch (TaskCanceledException)
{
return;
}
await Application.Current.Dispatcher.BeginInvoke(func);
});
}
I have this method:
private static async Task MyMethod();
And it is invocated this way:
public static void Main()
{
s_Finishing = false;
Task printTask = PrintStatistics();
MyMethod(serversSawa, serversSterling).Wait();
s_Finishing = true;
}
I expect that PrintStatistics will stop to run only after MyMethod is completed. But unfortunately it doesn`t. If I comment the line s_Finishing = true; The task runs forever - and allows to MyMethod to be completed
How can I solve the issue?
private static async Task PrintStatistics()
{
while (!s_Finishing)
{
long total = 0;
await Task.Delay(TimeSpan.FromSeconds(20));
foreach (var statistic in s_Statistics)
{
ToolsTracer.Trace("{0}:{1}", statistic.Key, statistic.Value);
total += statistic.Value;
}
foreach (var statistic in s_StatisticsRegion)
{
ToolsTracer.Trace("{0}:{1}", statistic.Key, statistic.Value);
}
ToolsTracer.Trace("TOTAL:{0}", total);
ToolsTracer.Trace("TIME:{0}", s_StopWatch.Elapsed);
}
}
private static async Task MyMethod()
{
Parallel.ForEach(
data,
new ParallelOptions { MaxDegreeOfParallelism = 20 }, async serverAndCluster =>
{
await someMethod() });
}
I believe your problem is here:
Parallel.ForEach(..., async ...);
You can't use async with ForEach. It's extremely rare to need to do both parallel (CPU-bound) and async (I/O-bound) together in the same method. If you just want concurrency (which I suspect), use Task.WhenAll instead of ForEach. If you really do need both CPU parallelism and async, then use TPL Dataflow.