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.
Related
I'm trying to understand async actions and I'm a bit confused.
Actions are just glorified Delegates. Given the Actions
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () => { await File.AppendAllLinesAsync("C:/Test.Txt",
new[] { "Async File Operation" });
};
How can we invoke this async seeing as one of the delegates is async and the other is not. I've seen some extension methods in other SO answers simplified for the example would look like so:
public static void InvokeAsync(this Action action, AsyncCallback ar, object userObject = null)
{
var listeners = action.GetInvocationList();
foreach (var t in listeners)
{
var handler = (Action)t;
handler.BeginInvoke(ar, userObject);
}
}
I'm concerned if this even works because it looks like it invokes your callback for each listener which doesn't make sense.
I've only been using async with the more friendly version async/await so I do not understand this syntax as much. (I'm assuming the callback would be everything after the await and the userObject is equivalent to the dreadful SyncronizationContext that causes deadlocks if when calling sync without ConfigureAwait(false), but that is just a guess)
This is syntax inconvenient so I would perfer to use async await syntax, since async/await is called using duck-typing. I've read a blog about using async with delegates which for the example
public static class DelegateExtensions
{
public static TaskAwaiter GetAwaiter(this Action action)
{
Task task = new Task(action);
task.Start();
return task.GetAwaiter();
}
}
This too concerns me for a few reason, this looks much like an anti pattern.
Isn't this just creating a task which will run my action synchronous on a seperate thread? I also don't see this run through the invocation list.
Are either of these methods proper for invoking run delegates asynchronously?
Is there a way I can invoke an async delegate with the await syntax while still fully leveraging async?
What is the proper way to invoke async delegates with multiple functions in the invocation list?
I think Eric Lippert's comment have clarified the situation more than I could ever.
Overall, if you need to act on the return type of a method, you shouldn't use multicast delegates. If you still have to, at least use a Func<Task> signature, then you can iterate on each individual delegate using GetInvocationList, as explained here.
But would it be really impossible to work your way out of a multicast delegate with async void method?
It turns out that you can be notified of beginning and end of async void methods by using a custom synchronization context and overriding the OperationStarted and OperationCompleted methods. We can also override the Post method to set the synchronization context of child operations, to capture subsequent async void calls.
Piecing it together, you could come with something like:
class Program
{
static async Task Main(string[] args)
{
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () =>
{
Callback();
await Task.Delay(1000);
Console.WriteLine("Async");
};
await AwaitAction(act);
Console.WriteLine("Done");
Console.ReadLine();
}
static async void Callback()
{
await Task.Delay(2000);
Console.WriteLine("Async2");
}
static Task AwaitAction(Action action)
{
var delegates = action.GetInvocationList();
var oldSynchronizationContext = SynchronizationContext.Current;
var asyncVoidSynchronizationContext = new AsyncVoidSynchronizationContext();
try
{
SynchronizationContext.SetSynchronizationContext(asyncVoidSynchronizationContext);
var tasks = new Task[delegates.Length];
for (int i = 0; i < delegates.Length; i++)
{
((Action)delegates[i]).Invoke();
tasks[i] = asyncVoidSynchronizationContext.GetTaskForLastOperation();
}
return Task.WhenAll(tasks);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
}
public class AsyncVoidSynchronizationContext : SynchronizationContext
{
private TaskCompletionSource<object> _tcs;
private Task _latestTask;
private int _operationCount;
public Task GetTaskForLastOperation()
{
if (_latestTask != null)
{
var task = _latestTask;
_latestTask = null;
return task;
}
return Task.CompletedTask;
}
public override void Post(SendOrPostCallback d, object state)
{
Task.Run(() =>
{
SynchronizationContext.SetSynchronizationContext(this);
d(state);
});
}
public override void OperationStarted()
{
if (Interlocked.Increment(ref _operationCount) == 1)
{
// First operation
_tcs = new TaskCompletionSource<object>();
_latestTask = _tcs.Task;
}
base.OperationStarted();
}
public override void OperationCompleted()
{
if (Interlocked.Decrement(ref _operationCount) == 0)
{
// Last operation
_tcs.TrySetResult(null);
}
base.OperationCompleted();
}
}
The output would be:
Sync
Async
Async2
Done
Of course, this code is provided just for recreational purpose. There's plenty of limitations, such as the fact the fact that it wouldn't work as-is if you're already using a synchronization context (such as the WPF one). I'm also certain that it has a few subtle bugs and concurrency issues here and there.
I have a code block which is eventually accessed by multiple threads. I search for an up to date async mechanism to continue executing when all threads have passed.
Currently I do the following with a CountDownEvent which works just fine (without async support).
public class Watcher
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private readonly CountdownEvent _isUpdating = new CountdownEvent(1);
private readonly IActivity _activity;
public Watcher([NotNull] IActivity activity)
{
_activity = activity ?? throw new ArgumentNullException(nameof(activity));
_activity.Received += OnReceived;
}
private void OnReceived(IReadOnlyCollection<Summary> summaries)
{
_isUpdating.AddCount();
try
{
// Threads processing
}
finally
{
_isUpdating.Signal();
}
}
private void Disable()
{
_activity.Received -= OnReceived;
_isUpdating.Signal();
/* await */ _isUpdating.Wait();
}
}
Do I need to use any of those AsyncCountdownEvent implementations or is there any other built-in mechanism? I already thought about using a BufferBlock because it has async functionality but I think it's a bit overkill.
Additional to the comments:
IActivity is a WebService call (but shouldn't effect the implementation on top or vice versa)
public async Task Start(bool alwayRetry = true, CancellationToken cancellationToken = new CancellationToken())
{
var milliseconds = ReloadSeconds * 1000;
do
{
try
{
var summaries = await PublicAPI.GetSummariesAsync(cancellationToken).ConfigureAwait(false);
OnSummariesReceived(summaries);
}
catch (Exception ex)
{
Log.Error(ex.Message);
OnErrorOccurred(ex);
}
await Task.Delay(milliseconds, cancellationToken).ConfigureAwait(false);
// ReSharper disable once LoopVariableIsNeverChangedInsideLoop
} while (alwayRetry);
}
It's not clear the IActivity signatures; but you can wait for a range of tasks to be completed:
class MultiAsyncTest {
Task SomeAsync1() { return Task.Delay(1000); }
Task SomeAsync2() { return Task.Delay(2000);}
Task EntryPointAsync() {
var tasks = new List<Task>();
tasks.Add(SomeAsync1());
tasks.Add(SomeAsync2());
return Task.WhenAll(tasks);
}
}
What's IActivity's signature? Does it support Task? Or you are using Thread? More explanation would help to a more specified answer.
So I've been searching StackOverflow/Google for different methods of running multiple async tasks concurrently. There seemed to be quite the debate between different methods and I just wanted to get some clarification. I'm writing a program to execute a JSON POST request until the server returns a status code of 200. Let's say I want to run 5 of theses tasks in parallel until one returns a status code of 200. Please try not to stray away from the topic, I have no control over the server! Here's my current code,
static bool status = false;
public static async Task getSessionAsync() {
while(!status) { ... }
}
public static async Task doMoreStuff() {
...
}
public static async Task RunAsync()
{
await getSessionAsync ();
await doMoreStuff();
}
public static void Main (string[] args)
{
Task.WhenAll(RunAsync()).GetAwaiter().GetResult();
}
Basically, I'm wondering if it's wrong for me to approach it like this,
public static async Task RunAsync()
{
for(int i = 0; i < 5; i++) {
await getSessionAsync ();
}
await doMoreStuff();
}
This will not run in parallel:
public static async Task RunAsync()
{
for(int i = 0; i < 5; i++) {
await getSessionAsync ();
}
await doMoreStuff();
}
You have to use Task.WhenAny()
public static async Task RunAsync()
{
var tasks = new List<Task>();
for(int i = 0; i < 5; i++) {
tasks.Add(getSessionAsync());
}
await Task.WhenAny(tasks);
await doMoreStuff();
}
If you do not need your current context (i.e. when you are writing a Library and not Frontend code), don't forget to use ConfigureAwait(false) after each await.
Assuming:
private Task<MySession> GetSessionAsync()
{
// ...
}
Option 1
Task.WhenAny
var session = await await Task.WhenAny(Enumerable.Range(0, 5).Select(_ => GetSessionAsync()));
Option 2
You could use the Rx LINQ method called Amb which will observe only the first Observable that returns something.
var session = await Enumerable.Range(0, 5).Select(_ => GetSessionAsync().ToObservable()).Amb().ToTask();
I have an async method
private async Task DoSomething(CancellationToken token)
a list of Tasks
private List<Task> workers = new List<Task>();
and I have to create N threads that runs that method
public void CreateThreads(int n)
{
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
for (int i = 0; i < n; i++)
{
workers.Add(DoSomething(token));
}
}
but the problem is that those have to run at a given time
public async Task StartAllWorkers()
{
if (workers.Count > 0)
{
try
{
while (workers.Count > 0)
{
Task finishedWorker = await Task.WhenAny(workers.ToArray());
workers.Remove(finishedWorker);
finishedWorker.Dispose();
}
if (workers.Count == 0)
{
tokenSource = null;
}
}
catch (OperationCanceledException)
{
throw;
}
}
}
but actually they run when i call the CreateThreads Method (before the StartAllWorkers).
I searched for keywords and problems like mine but couldn't find anything about stopping the task from running.
I've tried a lot of different aproaches but anything that could solve my problem entirely.
For example, moving the code from DoSomething into a workers.Add(new Task(async () => { }, token)); would run the StartAllWorkers(), but the threads will never actually start.
There is another method for calling the tokenSource.Cancel().
You can use TaskCompletionSource<T> as a one-time "signal" to asynchronous methods.
So you'd create it like this:
private TaskCompletionSource<object> _tcs;
public void CreateThreads(int n)
{
_tcs = new TaskCompletionSource<object>();
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
for (int i = 0; i < n; i++)
{
workers.Add(DoSomething(_tcs.Task, token));
}
}
Then when you're ready to start the tasks, just complete the "start" signal task:
public Task StartAllWorkers()
{
_tcs.TrySetCompleted(null);
return Task.WhenAll(workers);
}
(The StartAllWorkers method above has slightly different semantics than your original method: your original method would throw a cancellation exception as soon as the first task canceled; this one will wait until all the methods complete and then throw a cancellation exception)
Then your DoSomething just has to honor the "start signal":
private static async Task DoSomething(Task start, CancellationToken token)
{
await start;
... // rest of your code
}
What about this idea:
Instead of holding a list of tasks, hold a list of TaskReference items:
public class TaskReference
{
private readonly Func<Task> _func;
public TaskReference(Func<Task> func)
{
_func = func;
}
public async Task RunAsync()
{
await _func();
}
}
Adding to the list works like this:
taskList.Add(new TaskReference(() => DoSomething(myToken)));
And execution like this:
await Task.WhenAll(taskList.Select(o => o.RunAsync()));
I am trying to block RequestHandler.ParseAll() with await ConsumerTask;, but when i set a breakpoint there, i ALWAYS get the "Done..." output first... and then Parse2() fails with a NullReferenceException. (thats my guess: "the GC starts cleaning up because _handler got out of scope")
Anyway, I can't figure out why that happens.
class MainClass
{
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
/* fill mUrls here with values */
await Task.Run(() => _handler.ParseSpecific(mUrls));
Console.WriteLine("Done...");
}
}
static class Parser
{
public static async Task<IEnumerable<string>> QueryWebPage(string url) { /*Query the url*/ }
public static async Task Parse1(Query query)
{
Parallel.ForEach(/*Process data here*/);
}
public static async Task Parse2(Query query)
{
foreach(string line in query.WebPage)
/* Here i get a NullReference exception because query.WebPage == null */
}
}
sealed class RequestHandler
{
private BlockingCollection<Query> Queue;
private Task ConsumerTask = Task.Run(() => /* call consume() for each elem in the queue*/);
private async void Consume(Query obj)
{
await (obj.BoolField ? Parser.Parse1(obj) : Parser.Parse2(obj));
}
public async void ParseSpecific(string[] urls)
{
foreach(string v in urls)
Queue.Add(new Query(await QueryWebPage(v), BoolField: false));
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
private async Task ParseAll(bool onlySome)
{
ReInit();
Parallel.ForEach(mCollection, v => Queue.Add(new Query(url, BoolField:false)));
Queue.CompleteAdding();
await ConsumerTask;
/* Process stuff further */
}
}
struct Query
{
public readonly string[] WebPage;
public readonly bool BoolField;
public Query(uint e, IEnumerable<string> page, bool b) : this()
{
Webpage = page.ToArray();
BoolField = b;
}
}
CodesInChaos has spotted the problem in comments. It stems from having async methods returning void, which you should almost never do - it means you've got no way to track them.
Instead, if your async methods don't have any actual value to return, you should just make them return Task.
What's happening is that ParseSpecific is only running synchronously until the first await QueryWebPage(v) that doesn't complete immediately. It's then returning... so the task started here:
await Task.Run(() => _handler.ParseSpecific(mUrls));
... completes immediately, and "Done" gets printed.
Once you've made all your async methods return Task, you can await them. You also won't need Task.Run at all. So you'd have:
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
await _handler.ParseSpecific(mUrls);
Console.WriteLine("Done...");
}
...
public async TaskParseSpecific(string[] urls)
{
foreach(string v in urls)
{
// Refactored for readability, although I'm not sure it really
// makes sense now that it's clearer! Are you sure this is what
// you want?
var page = await QueryWebPage(v);
Queue.Add(new Query(page, false);
}
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
Your Reinit method also needs changing, as currently the ConsumerTask will basically complete almost immediately, as Consume will return immediately as it's another async method returning void.
To be honest, what you've got looks very complex, without a proper understanding of async/await. I would read up more on async/await and then probably start from scratch. I strongly suspect you can make this much, much simpler. You might also want to read up on TPL Dataflow which is designed to make producer/consumer scenarios simpler.