简体   繁体   中英

How to let the code wait to be executed until the other event handler finished?

In the following code, I want to use the geometryService (a utility class provided by third party) to sequentially calculate several shape area. I failed because the second time area calculation mustn't wait until the first calculation is finished. To resolve this issue, I can put the second area calculation into the finish calculation event handler, but the code is a mess. Is there a better way I can make geometryService.AreasAndLengthsAsync(secondShape) wait to execute until geometryService.AreasAndLengthsAsync(firstShape) is finished executing?

Shape firstShape = new Shape();
Shape secondShape = new Shape();

GeometryService geometryService = new GeometryService();
geometryService.AreaAndLengthsCompleted += GeometryService_AreasAndLengthsCompleted;
geometryService.AreasAndLengthsAsync(firstShape);
geometryService.AreasAndLengthsAsync(secondShape);

private void GeometryService_AreasAndLengthsCompleted(object sender, AreasAndLengthsEventArgs args){  }

You could place each Shape in a Queue<Shape>

Then kick off the first calculation, and in the completed handler check the queue for any other Shapes and if there is one, process it.

Additionally, the method you are calling is AreasAndLengthsAsync() . By convertion most API designers will include synchronous alternatives named the same byt without the Async part.. so look for AreasAndLengths() as an alternative.

This is a classic problem with async methods. If you're using the new MS Async CTP, you can encapsulate some of this stuff fairly cleanly, but if you're stuck using the traditional stuff, it's difficult to get clean code out of this.

One of the approaches that I take is to wrap the event-handler pattern with a continuation-passing pattern. It's not perfectly clean, but I like the look of the resulting code better. So you could do something like this:

public static void GetAreasAndLengthsAsync(Shape shape, Action<SomeResult> callback)
{
    var geometryService = new GeometryService();
    geometryService.AreasAndLengthsCompleted += (s, e) =>
    {
        if (callback != null)
        {
            callback(e.SomeResult);
        }
    };
    geometryService.AreasAndLengthsAsync(shape);
}

And you can then use it like this:

GetAreasAndLengthsAsync(firstShape, firstShapeResult =>
{
    GetAreasAndLengthsAsync(secondShape, secondShapeResult =>
    {
        DoSomethingWithTheseResults(firstShapeResult, secondShapeResult);
    });
});

Something like that, at any rate. The formatting is kinda ugly, but at least it expressed your intent fairly well. (Haven't compiled the code, there may be errors.)

If you don't like recreating the geometryService each time, you can do that at the field level in your class, and then pass the callback method as part the UserState parameter that most Async methods include.

You can use AutoResetEvent or ManualResetEvent, simply define one at the top of your class, and call Wait in the event that you want to wait for the other event, and then call Set in the event that you your waiting for, Wait will block until Set is called.

http://msdn.microsoft.com/en-us/library/system.threading.autoresetevent.aspx http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx

I would like to add that this is not ideal, but it you must have Serialized events and you are relying on a third party API than this may be the only option.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM