How to use Task<T> raising an event and waiting for event to be finished

随声附和 提交于 2019-12-18 09:05:37

问题


I have the following scenario:

  1. Client who is requesting a webservice to start

    public bool Start(MyProject project, string error)
    
  2. A web service who receives the call from the client in a method

    public event EventHandler<StartEventArgs> startEvent;
    
    public bool Start(MyProject project, string error)
    {
        Task<bool> result = StartAsync(project, error);
    
        return result.Result;
    }
    
    protected virtual void OnStart(StartEventArgs e)
    {
        // thread safe trick, snapshot of event
        var se = startEvent;
        if (se != null)
        {
            startEvent(this, e);
        }
    }
    
    private Task<bool> StartAsync(MyProject project, string error)
    {
        var taskCompletion = new TaskCompletionSource<bool>();
    
        this.startEvent += (p, e) => taskCompletion.TrySetResult((e.Error == string.Empty) ? true : false);
    
        this.OnStart(new StartEventArgs(project, error));
    
        return taskCompletion.Task;
    }
    
  3. An application that is subscribing to an event that is located in the web service:

    app.Start += EventHandler(App_Start)
    
    private bool App_Start()
    {
       // does something
       returns true/false
    }
    
  4. I want the web service to fire off the event in a Task, then wait for the function in the app.exe to finish and then return here to notify the user that the task has completed successfully.

I am not sure how to do this but in theory it would look something like this:

Task<bool> startTask = Task.Factory.StartNew(() => { OnStart() });
startTask.WaitAll(); // I think this is what I would need to for 4.0
return startTask.Result

I hope I am being descriptive enough for someone to see what I am trying to do. I would like the service to not have to know anything about the client and just run the task and once the event has finished its execution come back to this point and return to client a Boolean value representing success/failure.

Is this possible or am I taking a really wrong approach with this?

Update: Obviously OnStart is not an event so how do I do what you are trying to explain to me?


回答1:


You can wrap an event describing the Event based asynchronous pattern into an Task<T> via TaskCompletionSource<T>. The basic pattern is typically something like:

Task<bool> StartAsync()
{
    var tcs = new TaskCompletionSource<bool>();

    // When the event returns, set the result, which "completes" the task
    service.OnStarted += (o,e) => tcs.TrySetResult(e.Success);

    // If an error occurs, error out the task (optional)
    service.OnStartError += (o,e) => tcs.TrySetException(e.Exception);

    // Start the service call
    service.Start();

    // Return the Task<T>
    return tcs.Task;
}



回答2:


So I think I am now understanding how this needs to be done here is how I am doing it now.

Service code:

public void SetStartTask(Task<bool> startTask)
        {
            this.startTask = startTask;
        }

public bool Start(RtProjectInfo project, string error)
        {
            StartEventArgs args = new StartEventArgs(project, error);

            OnStart(args);

            return startTask.Result;
        }

protected virtual void OnStart(StartEventArgs e)
        {
            // thread safe trick, snapshot of event
            var se = startEvent;

            if (se != null)
            {
                startEvent(this, e);
            }
        }

public void StartFinished(MyProject project, string error)
        {
            OnStartFinish(new StartEventArgs(project, error));
        }

protected virtual void OnStartFinish(StartEventArgs e)
            {
                var sef = startFinished;

                if (sef != null)
                {
                    startFinished(this, e);
                }
            }

Here is a test client implementation

public void Start_Event(object sender, StartEventArgs e)
        {
            Task<bool> startTask = StartAsync();

            service.SetStartTask(startTask);

            DoOtherWork();
            DoOtherWork();
            DoOtherWork();
        }

private Task<bool> StartAsync()
        {
            var taskCompletion = new TaskCompletionSource<bool>();

            service.startFinished += (p, e) => 
            {
                taskCompletion.TrySetResult((e.Error == string.Empty) ? true : false); 
            };

            return taskCompletion.Task;
        }

private void DoingWork()
        {
            for(int i = 0; i < 100; ++i)
            {

            }

            service.StartFinished(project, error);
        }

        private void DoOtherWork()
        {
            for (int i = 0; i < 100000; ++i)
            {                    
            }
        }

So obviously whenever someone client calls DoingWork() the event will be received and everyone is happy! If anyone has any suggestions that are better please provide them as I am just learning how to use TPL correctly.



来源:https://stackoverflow.com/questions/29355731/how-to-use-taskt-raising-an-event-and-waiting-for-event-to-be-finished

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