I'm puzzled with this situation, where a class has a method that launches two periodic Tasks and then a property is used to check if both Tasks are still running or not, but the result is unexpected. Here is the code (simplified):
public partial class UdpClientConnector
{
Task localListener;
Task periodicSubscriber;
bool keepWorking = false;
public bool IsRunning
{
get
{
if ((localListener != null) && (periodicSubscriber != null))
{
return (localListener.Status == TaskStatus.Running) &&
(periodicSubscriber.Status == TaskStatus.Running);
}
else
return false;
}
}
public void Start()
{
keepWorking = true;
localListener = new Task(() => LocalListenerWorker());
localListener.Start();
periodicSubscriber = new Task(() => PeriodicSubscriberWorker());
periodicSubscriber.Start();
}
public void Stop()
{
keepWorking = false;
localListener.Wait();
periodicSubscriber.Wait();
}
async void LocalListenerWorker()
{
while (keepWorking)
{
// Do some work and then wait a bit
await Task.Delay(1000);
}
}
async void PeriodicSubscriberWorker()
{
while (keepWorking)
{
// Do some (other) work and then wait a bit
await Task.Delay(1000);
}
}
}
To test this boilerplate I used the following:
UdpClientConnector connector = new UdpClientConnector();
// This assert is successful because the two Tasks are not yet started
Assert.IsTrue(!connector.IsRunning);
// Starts the tasks and wait a bit
Connector.Start();
Task.Delay(2000).Wait();
// This fails
Assert.IsTrue(connector.IsRunning);
When I've tried to debug the test case, I've found that two Tasks are in the RanToCompletion state, which is unexpected due the fact that both tasks are just loops and should not terminate until keepWorking becomes false.
I've tried also to start the Tasks using Task.Factory.StartNew(..) with same results.
What I'm missing? Thank you!
The problem is with how you start the tasks, and indeed the task methods.
localListener = new Task(() => LocalListenerWorker());
That task will complete when LocalListenerWorker returns - which it will do pretty much immediately (when it hits the first await expression). It doesn't wait for the asynchronous operation to actually complete (i.e. the loop to finish).
async void methods should almost never be used - they're basically only there to support event handlers.
I suggest you rewrite your methods to return Task, and then use Task.Run to start them, passing in a method group:
Task.Run(LocalListenerWorker);
...
private async Task LocalListenerWorker()
{
// Body as before
}
The task by Task.Run will only complete when the task returned by LocalListenerWorker completes, which is when the loop body finishes.
Here's a complete demo:
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Task task1 = Task.Run(Loop);
// Don't do this normally! It's just as a simple demo
// in a console app...
task1.Wait();
Console.WriteLine("First task done");
Task task2 = new Task(() => Broken());
task2.Start();
// Don't do this normally! It's just as a simple demo
// in a console app...
task2.Wait();
Console.WriteLine("Second task done");
}
static async Task Loop()
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(1000);
Console.WriteLine(i);
}
}
static async void Broken()
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(1000);
Console.WriteLine(i);
}
}
}
The output shows:
0
1
2
3
4
First task done
Second task done
The first task behaves as expected, and only completes when the first async method has really completed. The second task behaves like your current code: it completes as soon as the second async method has returned - which happens almost immediately due to the await.
Related
I have a C# method, which calls an external web service multiple times, in a loop. I need to call it asynchronously in a different thread.
But the caller process MUST wait until the async thread meets a certain condition, - this condition occurs much before the loop iterations complete.
Please suggest a C# code example which describes how to wait until the async block of code indicates that a certain condition has been met, so that the main process can proceed without waiting for loop to finish.
My code:
..
List<MyObject> objList = GetObjects();
int counter = 0;
await Task.Factory.StartNew(() =>
{
foreach (MyObject obj in objList)
{
counter++;
CallExtWebSvc(obj);
if (counter == 1)
{
// return an indication that main process can proceed.
}
}
});
// Do other stuff...
You could execute your method as fire and forget and then wait for a TaskCompletionSource. This TaskCompletionSource is given to the method that calls the webservice as parameter. The method then sets a result on the TaskCompletionSource at some point.
Here is an example code piece:
public async Task DoWebserviceStuffAsync(TaskCompletionSource<bool> taskCompletionSource)
{
for (int i = 0; i < 10; i++)
{
//your webservice call
await Task.Delay(5000);
//some condition
if (i == 1)
{
//after setting this your CallingMethod finishes
//waiting the await taskCompletionSource.Task;
taskCompletionSource.TrySetResult(true);
}
}
}
private async Task CallerMethod()
{
var taskCompletionSource = new TaskCompletionSource<bool>();
//call method without await
//care: You cannot catch exceptions without await
DoWebserviceStuffAsync(taskCompletionSource);
//wait for the DoWebserviceStuffAsync to set a result on the passed TaskCompletionSource
await taskCompletionSource.Task;
}
If you want to avoid the danger of "fire and forget" or you also need to wait for the full operation to complete, you could return two tasks (Task,Task) (C# v7 syntax). The caller would await both tasks in order.
public async Task Caller()
{
var (partOne,partTwo) = DoSomethingAsync();
await partOne;
//part one is done...
await partTwo;
//part two is done...
}
public (Task,Task) DoSomethingAsync()
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
return (tcs.Task, DoWork(tcs));
}
public async Task DoWork(TaskCompletionSource<bool> tcs)
{
List<MyObject> objList = GetObjects();
int counter = 0;
await Task.Run(() =>
{
foreach (MyObject obj in objList)
{
counter++;
CallExtWebSvc(obj);
if (counter == 1)
{
// return an indication that main process can proceed.
tcs.SetResult(true);
}
}
});
// Do other stuff...
}
I have two methods I want to call within a loop. Step1() has to complete before Step2() is called. But in a loop, Step1() can start while Step2() is asynchronously executing. Should I simply wait for the Step2 task, before allowing any other 'Step2' tasks from being executed, as I do in the code below?
public MainViewModel()
{
StartCommand = new RelayCommand(Start);
}
public ICommand StartCommand { get; set; }
private async void Start()
{
await Task.Factory.StartNew(() =>
{
Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Started processing.");
for (int i = 0; i < 10; i++)
{
_counter++;
string result = Step1(i);
_step2Task?.Wait(); //Is this OK to do???
Step2(result).ConfigureAwait(false);
}
_step2Task?.Wait();
Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Finished processing.");
});
}
private string Step1(int i)
{
Thread.Sleep(5000); //simulates time-consuming task
Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Step 1 completed - Iteration {i}.");
return $"Step1Result{i}";
}
private async Task Step2(string result)
{
_step2Task = Task.Run(() =>
{
Thread.Sleep(4000); //simulates time-consuming task
Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Step 2 completed. - {result}");
});
await _step2Task;
}
Don't do any of this stuff; you will risk getting deadlocks all over the place. Also, don't move stuff onto threads unless it is CPU bound.
Start over:
Find every long-running synchronous method that is CPU intensive and write an async wrapper around it. The async wrapper should grab a worker thread, execute the CPU intensive task, and complete when the execution is done. Now you consistently have an abstraction in terms of tasks, not threads.
Move all of your control flow logic onto the UI thread.
Put an await everywhere that you mean "the code that comes after this must not execute until the awaited task is complete".
If we do that, your code gets a lot simpler:
// Return Task, not void
// Name async methods accordingly
private async Task StartAsync()
{
Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Started processing.");
Task task2 = null;
for (int i = 0; i < 10; i++)
{
// We cannot do Step2Async until Step1Async's task
// completes, so await it.
string result = await Step1Async(i);
// We can't run a new Step2Async until the old one is done:
if (task2 != null) {
await task2;
task2 = null;
}
// Now run a new Step2Async:
task2 = Step2Async(result);
// But *do not await it*. We don't care if a new Step1Async
// starts up before Step2Async is done.
}
// Finally, don't complete StartAsync until any pending Step2 is done.
if (task2 != null) {
await task2;
task2 = null;
}
Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Finished processing.");
}
private string Step1(int i)
{
// TODO: CPU intensive work here
}
private async Task<string> Step1Async(int i) {
// TODO: Run CPU-intensive Step1(i) on a worker thread
// return a Task<string> representing that work, that is
// completed when the work is done.
}
private void Step2(string result)
{
// TODO: CPU-intensive work here
}
private async Task Step2Async(string result)
{
// TODO: Again, make a worker thread that runs Step2
// and signals the task when it is complete.
}
Remember, await is the sequencing operation on workflows. It means don't proceed with this workflow until this task is complete; go find some other workflow.
Exercise: How would you write the code to represent the workflow:
Step1 must complete before Step2
Any number of Step2 may be running at the same time
All the Step2 must complete before Start completes
?
I've read this: Is it ok to await the same task from multiple threads - is await thread safe? and I don't feel clear about the answer, so here's a specific use case.
I have a method that performs some async network I/O. Multiple threads can hit this method at once, and I dont wan't them all to invoke a network request, If a request is already in progress I want to block/await the 2nd+ threads, and have them all resume once the single IO operation has completed.
How should I write the following pseudcode?
I'm guessing each calling thread really needs to get its own Task, so each can get it's own continuation, so instead of returning currentTask I should return a new Task which is completed by the "inner" Task from DoAsyncNetworkIO.
Is there a clean way to do this, or do I have to hand roll it?
static object mutex = new object();
static Task currentTask;
async Task Fetch()
{
lock(mutex)
{
if(currentTask != null)
return currentTask;
}
currentTask = DoAsyncNetworkIO();
await currentTask;
lock(mutex)
{
var task = currentTask;
currentTask = null;
return task;
}
}
You could use a SemaphoreSlim to ensure that only one thread actually executes the background thread.
Assume your base task (the one actually doing the IO) is in a method called baseTask(), which I shall emulate like so:
static async Task baseTask()
{
Console.WriteLine("Starting long method.");
await Task.Delay(1000);
Console.WriteLine("Finished long method.");
}
Then you can initialise a SemaphoreSlim like so, to act a bit like an AutoResetEvent with initial state set to true:
static readonly SemaphoreSlim signal = new SemaphoreSlim(1, 1);
Then wrap the call to baseTask() in a method that checks signal to see if this is the first thread to try to run baseTask(), like so:
static async Task<bool> taskWrapper()
{
bool firstIn = await signal.WaitAsync(0);
if (firstIn)
{
await baseTask();
signal.Release();
}
else
{
await signal.WaitAsync();
signal.Release();
}
return firstIn;
}
Then your multiple threads would await taskWrapper() rather than awaiting baseTask() directly.
Putting that altogether in a compilable console application:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
static class Program
{
static void Main()
{
for (int it = 0; it < 10; ++it)
{
Console.WriteLine($"\nStarting iteration {it}");
Task[] tasks = new Task[5];
for (int i = 0; i < 5; ++i)
tasks[i] = Task.Run(demoTask);
Task.WaitAll(tasks);
}
Console.WriteLine("\nFinished");
Console.ReadLine();
}
static async Task demoTask()
{
int id = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Thread {id} starting");
bool firstIn = await taskWrapper();
Console.WriteLine($"Task {id}: executed: {firstIn}");
}
static async Task<bool> taskWrapper()
{
bool firstIn = await signal.WaitAsync(0);
if (firstIn)
{
await baseTask();
signal.Release();
}
else
{
await signal.WaitAsync();
signal.Release();
}
return firstIn;
}
static async Task baseTask()
{
Console.WriteLine("Starting long method.");
await Task.Delay(1000);
Console.WriteLine("Finished long method.");
}
static readonly SemaphoreSlim signal = new SemaphoreSlim(1, 1);
}
}
(The methods are all static because they are in a console app; in real code they would be non-static methods.)
await doesn't necessarily use continuations (the Task.ContinueWith kind) at all. Even when it does, you can have multiple continuations on one Task - they just can't all run synchronously (and you might run into some issues if you have a synchronization context).
Do note that your pseudo-code isn't thread-safe, though - you can't just do currentTask = DoAsyncNetworkIO(); outside of a lock. Only the await itself is thread-safe, and even then, only because the Task class that you're awaiting implements the await contract in a thread-safe way. Anyone can write their own awaiter/awaitable, so make sure to pay attention :)
I run several tasks and keep them in a list to check if they are already completed.
I discovered that tasks that come from an async method are always shown as RanToCompletion although the task itself was still running.
Is there a way to get the "is complete" information from a Task object in both cases?
Here's a simple test case that shows this behaviour. I run two tasks, with/without an async method and check the states during and after completion.
private void test()
{
;
Action actionAsync = funcAsync;
Task taskAsync = Task.Run(actionAsync);
Action action = func;
Task task = Task.Run(action);
var statusAsync = taskAsync.Status;
var status = task.Status;
// stati are either WaitingToRun or Running
Thread.Sleep(TimeSpan.FromSeconds(2));
// Now it's quite certain, that both have started
var statusAsync2 = taskAsync.Status;
var status2 = task.Status;
Debug.Assert(statusAsync2 == TaskStatus.RanToCompletion);
Debug.Assert(status2 == TaskStatus.Running);
;
Thread.Sleep(TimeSpan.FromSeconds(12));
// Now it's quite certain, that both have finished
var statusAsync3 = taskAsync.Status;
var status3 = task.Status;
;
Debug.Assert(statusAsync3 == TaskStatus.RanToCompletion);
Debug.Assert(status3 == TaskStatus.RanToCompletion);
}
private async void funcAsync()
{
await Task.Delay(TimeSpan.FromSeconds(10));
}
private void func()
{
Thread.Sleep(TimeSpan.FromSeconds(10));
}
I discovered that tasks that come from an async method are always shown as RanToCompletion although the task itself was still running.
Yes, because your void method has completed, and that's all that Task.Run is calling. If instead you use:
private async Task FuncAsync()
{
await Task.Delay(TimeSpan.FromSeconds(10));
}
and use Func<Task> instead Action, then you'll call Task.Run(Func<Task>) and all will be well.
Short but complete example:
using System;
using System.Threading;
using System.Threading.Tasks;
class Test
{
static void Main()
{
Func<Task> func = FuncAsync;
Task task = Task.Run(func);
for (int i = 0; i < 7; i++)
{
Console.WriteLine(task.Status);
Thread.Sleep(1000);
}
}
private static async Task FuncAsync()
{
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
Output:
WaitingForActivation
WaitingForActivation
WaitingForActivation
WaitingForActivation
WaitingForActivation
RanToCompletion
RanToCompletion
Try to avoid writing void async methods if you possibly can. They should basically only be used for event handlers.
I have a producer–consumer multiply task Class.
I have a method:
private async Task Consume(CancellationToken cancellationToken){..}
There is a starting method:
public void Run()
{
var workerCount = Session.GetParameters().GetThreadCount();
_workers = new List<Task>(workerCount);
for (var i = 0; i < workerCount; i++)
_workers.Add(Consume(StopCancellationTokenSource.Token));
Task.WhenAll(_workers).ContinueWith(_ => DoneEvent);
}
The problem is that DoneEvent is fired, but subscriber event handler never get executed.
The only way that I succeeded to run event handler is where I made the Run method async and added await before Task.WhenAll(..). But then another issue raised. In the method Consume() I have a ManualResetEvent PauseBlock. And when it is reset the main thread waits also.
Thnx in advance.
EDIT:
I have managed to do it right (after two days)
I have changed a little bit the Run method:
public async void Run()
{
var workerCount = Session.GetParameters().GetThreadCount();
_workers = new List<Task>(workerCount);
for (var i = 0; i < workerCount; i++)
_workers.Add(Task.Run(()=> Consume(StopCancellationTokenSource.Token)));
await Task.WhenAll(_workers);
DoneEvent();
}
Now it is working correctly.
Just for info Consumer method :
private async Task Consume(CancellationToken cancellationToken)
{
try
{
await Task.Delay(5000, cancellationToken);
IEngGroup engGroup;
while (Groups.TryDequeue(out engGroup))
{
cancellationToken.ThrowIfCancellationRequested();
if (!engGroup.IsEnabled || engGroup.Result.Status == ItemRunningStatus.Successful) continue;
if (engGroup.IsBreak) Pause();
//if paused wait
PauseBlock.WaitOne();
//if stoped throw
cancellationToken.ThrowIfCancellationRequested();
var groupRunner = new GroupRunner(cancellationToken, PauseBlock);
if (engGroup.FireAndForget)
groupRunner.RunGroup(engGroup);
else
await groupRunner.RunGroup(engGroup);
}
return;
}
catch (OperationCanceledException)
{
return ;
}
}
Thnx to all of you.
If you have any suggestion on ow to improve, I would like to see it.
Try this:
Run().ConfigureAwait(false);
public async Task Run()
{
...
await Task.WhenAll(_workers).ContinueWith(_ => DoneEvent);
}
So this will tell the run it doesn't need to return to the current thread again and finish executing on what ever thread it went onto. It won't block the main thread.
But I must warn you, any code ran on the "DoneEvent" will be executed on the other thread, so if you have any code which affects, for example the UI Thread, you will get a Invalid cross-thread access exception.