Async Pattern - waiting for an Event before returning some value from a method

别来无恙 提交于 2020-05-15 06:53:05

问题


[Disclaimer - this code is simplified (a lot) to easy reading and I know it doesent conform to normal code standards]

My problem can bee seen in the code below. Basically I have a caller that parses in an object. I have to wait until a subcomponent is finished - which is signaled by an event - before returning a value from that that is based on some value on the subcomponent.

The question is: What is the preferred pattern for situations like this (of course an actual solution would be most welcome).

I've experimented with different stuff around TaskCompletionSource etc. but I'm afraid my understanding is lagging to much to find an (preferably) elegant solution. Hope you can help.

public class AsyncEventTest
{
    // This is performed one a single (UI) thread. The exception to this is
    // a.B that might - at the calling time - get a asycronious update from the backend.
    // The update is syncronized into the calling context so Task.Wait etc. will effectivly
    // deadlock the flow.
    public static string CallMe(A a)
    {
        if (a.B.State != State.Ready)
        {
            // wait for a.B.State == State.Ready ... but how ...
            // await MagicMethod() ???;
        }

        // only execute this code after a.b.State == State.Ready
        return a.B.Text;
    }
}

public class A
{
    public B B { get; set; }
}

public class B
{
    public State State { get; private set; }
    public event Action StateChanged;
    public string Text { get; }
}

public enum State { Ready, Working, }

EDIT - example of what I tried I wonder if something like this is an acceptable approach (or if it even works)?

public class AsyncEventTest2
{
    public static string CallMe(A a)
    {
        return CallMe1(a).Result;
    }

    public async static Task<string> CallMe1(A a)
    {
        await CallMe2(a);
        return a.B.Text;
    }

    public static Task CallMe2(A a)
    {
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
        if (a.B.State != State.Ready)
        {
            a.B.StateChanged += () =>
                {
                    if (a.B.State == State.Ready)
                        tcs.SetResult(a.B.Text);
                };
        }
        else
        {
            tcs.SetResult(a.B.Text);
        }

        return tcs.Task;
    }
}

回答1:


You can register for the StateChanged event and use a TaskCompletionSource.

public static Task WaitForReady(this B b)
{
    TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
    Action handler = null;
    handler = () =>
    {
        if (b.State == State.Ready)
        {
            b.StateChanged -= handler;
            tcs.SetResult(null);
        }
    };

    b.StateChanged += handler;
    return tcs.Task;
}

Beware there could be a race if the event could be raised before the handler is registered.



来源:https://stackoverflow.com/questions/14242129/async-pattern-waiting-for-an-event-before-returning-some-value-from-a-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!