Await on event handler - c#

So here's deleagte and event
public delegate Task SomeEventHandler(SomeEventArgs e);
...
public event SomeEventHandler OnSomething;
Subscribers (multiple)
some.OnSomething += DoSomething;
...
public async Task DoSomething(SomeEventArgs e) {
await SomethingElse();
e.A = true;
}
Event call
if (this.OnSomething != null)
await this.OnSomething(args);
// Here args.A is false
// It should be true
The problem is that last part continues even when DoSomething isn't finished. What would be the problem?

The problem here is that multiple instances of SomeEventHandler are running hence there are multiple Task values being created. The await call is only running on one of them hence it's somewhat up to chance as to whether or not it's theDoSomething method that ends up being awaited.
To fix this you will need to await on every Task value that is created
if (this.OnSomething != null) {
foreach (var d in this.OnSomething.GetInvocationList().Cast<SomeEventHandler>()) {
await d(args);
}
]

Related

Best way to cancel long running process inside backgroundworker

What is the best solution to quickly cancel long running processes inside background worker?
For example, we have such situation:
private void DoWork(object sender, DoWorkEventArgs e)
{
...
for (int i = 0; i < items; i++)
{
if (_worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
VeryLongRunningProcess();
}
}
}
private void VeryLongRunningProcess()
{
var a = Test();
var b = Test2();
Thread.Sleep(5000);
var c = Test3();
}
In such case, VeryLongRunningProcess() will be not finished on pressing cancel until he finished everything inside his body.
What to do in such cases?
I tried to pass (BackgroundWorker)sender to VeryLongRunningProcess() as param and inside this method check for CancellationPending, but i dont know is this correct way or not
If the problem is isolated your VeryLongRunningProcess from classes like the worker, you can use a Func as a parameter and leave outside your method the worker access
private void VeryLongRunningProcess(Func<bool> isCancelled)
{
var a = Test();
if (isCancelled())
{
return;
}
var b = Test2();
if (isCancelled())
{
return;
}
Thread.Sleep(5000);
var c = Test3();
}
Inside your method, you may check if you must cancel the operation as many times you need. And you can use the Func as a parameter in other methods like Test1, Test2... if any of them takes long time to finish.
Then, you invoke your method in this form:
VeryLongRunningProcess(() => _worker.CancellationPending);
As other people comment, maybe interesting use async/await.
UPDATE
Another way to do if you want choose the use or not of the cancellation:
private void VeryLongRunningProcess(Func<bool> isCancelled = null)
{
var a = Test();
// Or: isCancelled != null && isCancelled()
if (isCancelled?.Invoke() ?? false)
{
return;
}
// ...
}
Normally you should create long-running process as "async" method (public async Task or Task DoWork()) for resources destribution purposes. "CancelationToken" enables cooperative cancellation between threads, thread pool work items. Also it is possible to propagate a callback delegate that can be invoked when Cancellation Token cancelled or function is compleete.

Async method is not executed in the right (expected) order

I have a component with the following event:
// MyComponent :
[Parameter] public EventCallback<DataChangedEventArgs> OnTextChanged { get; set; }
protected async void SomeMethod(ChangeEventArgs args)
{
DataChangedEventArgs evArg =new DataChangedEventArgs(args.value);
Console.Writeline("1");
await OnTextChanged.InvokeAsync(evArg);
Items = evArg.Data; // <=== problem here: Data is always null.
Console.Writeline("4");
}
When I use and handle the OnTextChanged event on another component, I set the Data property of DataChangedEventArgs:
// AnotherComponent that uses MyComponent:
public async void theTextChanged(DataChangedEventArgs args)
{
//...
Console.Writeline("2");
args.Data = await GetAPersonAsync(); // or some other object
Console.Writeline("3");
}
Now I expect Items to be set to the Person (the line Items = evArg.Data). But Data is always null, which means the changes that have been made to args.Data(which is a reference type) in event handler are not accessible in the invoking method the SomeMethod.
Can anyone help me with that ?
UPDATE: I think it has something to do with async method, as the console result is not as I expect (1,2,3,4):
console:
1
2
4
3
You should use async Task instead of async void.
Theodor provided a link to the documentation
To add some meat to #Mister Magoo's answer, I believe the Blazor code for the event handlers looks something like this (simplified for clarity):
var task = InvokeAsync(EventMethod);
StateHasChanged();
if (task.Status != TaskStatus.RanToCompletion && task.Status != TaskStatus.Canceled)
{
await task;
StateHasChanged();
}
If your EventMethod returns a void, InvokeAsync returns a completed Task as soon as EventMethod yields. Only one StateHasChanged gets called - the if block is skipped. Anything that happens in EventMethod after the yield is not reflected in the UI until another component UI update occurs.

async await WhenAll not waiting

I'm developing WinForms App in VS2013, .NET FW 4.5.1. Here is my reduced code with inline comments about structure:
// Progress object implementing IProgress<MyProgressData>
var progressCallback = new Progress<MyProgressData>();
// listOfMyList is actually List<List<MyObject>>, which contains list of
// list of MyObject's which will be executed as tasks at once.
// For example, this would be sample structure for list of lists:
// List1
// MyObject1
// MyObject2
// MyObject3
// List2
// MyObject4
// MyObject5
// MyObject6
// List1's and List2's objects would be executed as all tasks at once, but List1 and List2 respectively
// would be executed one after another (because of resources usage inside TASK CODE)
foreach (var myItem in listOfMyList)
{
var myList = myItem.ToList();
// Create a list of tasks to be executed (20 by default; each taking from 30-60 seconds)
// Here cs is actually MyObject
var myTasks = myList.Select(cs => Task.Run(async () =>
{
// TASK CODE (using cs as an input object and using "cancellationToken.ThrowIfCancellationRequested();" inside execution to cancel executing if requested)
}, cancellationToken));
await Task.WhenAll(myTasks); // Wait for all tasks to finish
// Report progress to main form (this actually calls an event on my form)
await Task.Run(() => progressCallback.Report(new MyProgressData() { props }), CancellationToken.None);
}
As you can see, I construct progress object and then I have list of lists. Each item within top-level list should execute in serialized fashion (one after another). Each item's list elements, should execute all at once in a form of a tasks.
So far so good, all tasks start and even WhenAll waits for them. Or at least I thought so. I have put logging inside relevant methods, to show me code execution. It turns out that while progress logic (at the bottom) is executing, foreach loop starts executing another batch of tasks, which it shouldn't.
Am I missing something here? Does code for progress not block or wait for Report method to finish executing. Maybe I'm missing sth about async/await. With await we make sure that code won't continue until after method is finished? It won't block current thread, but it also won't continue executing?
Is it even possible (since its happening, it probably is), for my foreach loop to continue executing while progress reporting is still on the go?
This code resides inside an async method. It's actually called like this (lets assume this method is async MyProblematicMethod()):
while (true)
{
var result = await MyProblematicMethod();
if (result.HasToExitWhile)
break;
}
Every method up from MyProblematicMethod uses await to wait for async methods, and is not called many times.
Based on Glorin's suggestion that IProgress.Report returns immediately after firing an event handler, I've created exact copy of Progress class, which uses synchronizationContext.Send instead of Post:
public sealed class ProgressEx<T> : IProgress<T>
{
private readonly SynchronizationContext _synchronizationContext;
private readonly Action<T> _handler;
private readonly SendOrPostCallback _invokeHandlers;
public event EventHandler<T> ProgressChanged;
public ProgressEx(SynchronizationContext syncContext)
{
// From Progress.cs
//_synchronizationContext = SynchronizationContext.CurrentNoFlow ?? ProgressStatics.DefaultContext;
_synchronizationContext = syncContext;
_invokeHandlers = new SendOrPostCallback(InvokeHandlers);
}
public ProgressEx(SynchronizationContext syncContext, Action<T> handler)
: this(syncContext)
{
if (handler == null)
throw new ArgumentNullException("handler");
_handler = handler;
}
private void OnReport(T value)
{
// ISSUE: reference to a compiler-generated field
if (_handler == null && ProgressChanged == null)
return;
_synchronizationContext.Send(_invokeHandlers, (object)value);
}
void IProgress<T>.Report(T value)
{
OnReport(value);
}
private void InvokeHandlers(object state)
{
T e = (T)state;
Action<T> action = _handler;
// ISSUE: reference to a compiler-generated field
EventHandler<T> eventHandler = ProgressChanged;
if (action != null)
action(e);
if (eventHandler == null)
return;
eventHandler((object)this, e);
}
}
This means that ProgressEx.Report will wait for method to finish, before returning. Maybe not the best solution in all situations, but it worked for me in this case.
To call it, just create ProgressEx with SynchronizationContext.Current as a parameter to constructor. However, it must be created in UI thread, so the right SynchronizationContext gets passed in. E.g. new ProgressEx<MyDataObject>(SynchronizationContext.Current)

Async EventHandlers in a HttpModule

How do you set them up?
If I have the following code in a HttpModule.
public static event EventHandler<PostProcessingEventArgs> OnPostProcessing;
And in an async PostAuthorizeRequest task set up using EventHandlerTaskAsyncHelper.
// Fire the post processing event.
EventHandler<PostProcessingEventArgs> handler = OnPostProcessing;
if (handler != null)
{
handler(this, new PostProcessingEventArgs { CachedPath = cachedPath });
}
And then tap into it using this.
ProcessingModule.OnPostProcessing += this.WritePath;
private async void WritePath(object sender, PostProcessingEventArgs e)
{
await Task.Factory.StartNew(() => Debug.WriteLine(e.CachedPath));
}
I get the following error.
An asynchronous module or handler completed while an asynchronous
operation was still pending.
Edit
Ok so before I saw all these answers I got it to not throw the error by raising the event handler as follows.
EventHandlerTaskAsyncHelper postProcessHelper =
new EventHandlerTaskAsyncHelper(this.PostProcessImage);
context.AddOnPostRequestHandlerExecuteAsync(postProcessHelper.BeginEventHandler,
postProcessHelper.EndEventHandler);
private Task PostProcessImage(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
object cachedPathObject = context.Items[CachedPathKey];
if (cachedPathObject != null)
{
string cachedPath = cachedPathObject.ToString();
// Fire the post processing event.
EventHandler<PostProcessingEventArgs> handler = OnPostProcessing;
if (handler != null)
{
context.Items[CachedPathKey] = null;
return Task.Run(() => handler(this,
new PostProcessingEventArgs { CachedImagePath = cachedPath }));
}
}
return Task.FromResult<object>(null);
}
From what I can see below though this seems unwise.
The single purpose of this eventhandler would be to allow someone to run longer running tasks on the file like using something like jpegtran or pngout to post-process an image to further optimize it. What's the best approach for that?
You can add async event handlers using the AddOn* methods in the HttpApplication class. I'm sure that async void methods are not supported by all of them. Maybe by none of them.
To use those methods despite the fact that they do not directly support tasks, you need to adapt your task to be compatible with the APM pattern which ASP.NET uses here.
Maybe it is just sample code but you use of Task.Factory.StartNew is not helpful in the context of a web application.
The key is that you need to avoid async void. There are a couple of places where async void can trip you up.
You're already handling the first one correctly by using EventHandlerTaskAsyncHelper. I assume your setup code looks something like this:
public void Init(HttpApplication context)
{
var helper = new EventHandlerTaskAsyncHelper(InvokePostAuthEvents);
context.AddOnPostAuthorizeRequestAsync(helper.BeginEventHandler,
helper.EndEventHandler);
}
With this kind of setup, you're avoiding an async void PostAuthorizeRequest.
The other side is when you raise the OnPostProcessing event. This is where you are having problems with async void. There are a variety of ways to raise async-aware events (I cover a number of them on my blog), but I prefer the "deferral" method which is used by WinStore apps, so it will likely be more familiar to developers.
I have a DeferralManager in my AsyncEx library that is designed to be used in your event args like this:
public class PostProcessingEventArgs
{
private readonly DeferralManager _deferrals;
public PostProcessingEventArgs(DeferralManager deferrals, ...)
{
_deferrals = deferrals;
...
}
public IDisposable GetDeferral()
{
return deferrals.GetDeferral();
}
...
}
When you raise the event, you do this:
Task RaisePostProcessingEventAsync()
{
EventHandler<PostProcessingEventArgs> handler = OnPostProcessing;
if (handler == null)
return TaskConstants.Completed;
var deferrals = new DeferralManager();
var args = new PostProcessingEventArgs(deferrals) { CachedPath = cachedPath };
handler(this, args);
return deferrals.SignalAndWaitAsync();
}
Note that raising the event is now an asynchronous operation, since it will (asynchronously) wait for all the event handler deferrals to complete.
Regular (synchronous) event handlers require no changes, but asynchronous event handlers need to use a deferral, like this:
private async void WritePath(object sender, PostProcessingEventArgs e)
{
using (e.GetDeferral())
{
await Task.Delay(1000);
Debug.WriteLine(e.CachedPath);
}
}
As a final note, neither StartNew nor Run is a good idea on ASP.NET. If you have synchronous code to run, just run it directly.
It's complaining that the worker thread hasn't completed before the request thread is terminated. This is a no no... for all it knows your worker thread might not ever terminate, which would lead to thread starvation in a very short space of time.
If you want to have a worker thread you need to create it in the Init of your HttpModule. I think this is a pretty good example...
http://msdn.microsoft.com/en-us/library/hh567803(v=cs.95).aspx
So have a single worker thread that runs for the duration of the module and and let the requests simply add work for the worker thread to process

C#: How do I wait for an event to be set?

I want to write a synchronous test that calls into some asynchronous product tasks.
In the example below, DoSomething() is called by a separate thread, and then it sets the SomethingCompleted event.
In my test code, how do I wait for SomethingCompleted to be set?
public event Action<Result> SomethingCompleted;
public void DoSomething()
{
Something();
this.SomethingCompleted(new Result("Success"));
}
using (var evt = new ManualResetEvent()) {
Action<Result> handler = _ => evt.Set();
SomethingCompleted += handler;
evt.WaitOne();
SomethingCompleted -= handler; //cut object reference to help GC
}
If required you can unsubscribe from the event after the wait has completed. That way the event will not keep the delegate and closure instance alive.
You can extract this into a reusable helper method/extension.
// test case
public void Test()
{
var yourObj = new YourObj();
var done = false;
Result result;
yourObj.SomethingCompleted += (finalResult) => {
result=finalResult;
done=true;
};
yourObj.DoSomething();
while(!done) Thread.Sleep(200);
if(result != theExpectedResult) kaboom();
}
What about subscribing to an event and "polling" the lambda until result comes available? This should work.
You're using the wrong type of event. The event you're using is a callback. In the code you supplied, the delegates attached to the SomethingCompleted event are going to be called on the same thread as DoSomething.
What you want is thread synchronization, using an event like AutoResetEvent or ManualResetEvent, which have nothing to do with the framework/language-level event that you're using. You could do something like this:
void MainThreadProc()
{
// create thread synchronization event
using (var evt = new ManualResetEvent(false))
{
// start background operation
ThreadPool.QueueUserWorkItem(BackgroundThreadProc, evt);
// this line will block until BackgroundThreadProc sets the event
evt.WaitOne();
}
// resume main thread operations here
...
}
void BackgroundThreadProc(object o)
{
// get event from argument
var evt = (ManualResetEvent) o;
// do the deed
...
// set event to signal completion
evt.Set();
}
This is just one of a number of different ways to do this. Alternatives include Parallel LINQ or the Task Parallel Library (both of which are best used with parallel operations, not just a single background operation). If you don't want to block the main thread, look at BackgroundWorker.

Categories