如何使用Task <t>引发事件并等待事件完成</t>

时间:2015-03-30 20:31:40

标签: c# multithreading asynchronous event-handling task-parallel-library

我有以下情况:

  1. 请求启动网络服务的客户

    public bool Start(MyProject project, string error)
    
  2. 通过方法

    接收来自客户端的呼叫的Web服务
    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. 订阅位于Web服务中的事件的应用程序:

    app.Start += EventHandler(App_Start)
    
    private bool App_Start()
    {
       // does something
       returns true/false
    }
    
  4. 我希望Web服务在任务中触发事件,然后等待app.exe中的函数完成,然后返回此处以通知用户该任务已成功完成。

  5. 我不知道该怎么做但理论上它看起来像这样:

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

    我希望我的描述能够让某人看到我想要做的事情。我希望服务不必了解客户端,只需运行任务,一旦事件完成执行,就会返回到客户端,返回一个表示成功/失败的布尔值。

    这是可能的还是我采取了一种非常错误的做法?

    更新 显然OnStart不是一个事件,所以我该怎么做你要向我解释的内容?

2 个答案:

答案 0 :(得分:2)

您可以通过TaskCompletionSource<T>将描述基于事件的异步模式的事件包装到Task<T>中。基本模式通常类似于:

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;
}

答案 1 :(得分:1)

所以我想我现在明白这里需要做的是我现在是怎么做的。

服务代码:

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);
                }
            }

这是一个测试客户端实现

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)
            {                    
            }
        }

很明显,每当有人打电话给DoingWork()时,都会收到活动,大家都很开心!如果有人有任何更好的建议请提供,因为我正在学习如何正确使用TPL。