控制异步调用中的流程

时间:2013-12-10 08:06:46

标签: c# .net

我在理解异步流程中如何处理程序控制方面遇到了困难。

我有一个SessionManager类,它会调试会话,我们需要注册 对于事件OnStartApplicationSessionResponse,我的控件将返回到调用点。我会在一段时间后在eventhandler中获取会话ID,如果出现错误,则会收到错误代码。

class SessionManager
{   
    public bool startUp(Object params)
    {
        try
        {
            serviceProvider = new ServiceProvider();
            serviceProvider.OnStartApplicationSessionResponse += new StartApplicationSessionResponseHandler(ServiceProvider_OnStartApplicationSessionResponse);
            serviceProvider.startUp(params);
        }
    }

    public void ServiceProvider_OnStartApplicationSessionResponse(object sender, ServiceProvider.StartApplicationSessionResponseArgs e)
    {
        //e.getError
        //I will get the session Id here or error code
    }
}

当我的控件现在处于呼叫位置时,如何获取sessionId或错误?

2 个答案:

答案 0 :(得分:2)

您可以使用TaskCompletionSource使事件等待。

class SessionManager
{
    private ServiceProvider _serviceProvider;

    public int SessionId
    {
        get;
        private set;
    }

    public Task<bool> StartUp(Object param)
    {
        _serviceProvider = new ServiceProvider();
        var tcs = new TaskCompletionSource<bool>();

        _serviceProvider.OnStartApplicationSessionResponse += (sender, args) =>
        {
            // do your stuff
            // e.g.
            SessionId = 0xB00B5;
            tcs.SetResult(true);
        };
        _serviceProvider.startUp(param);

        return tcs.Task;
    }
}

电话会是:

private static async void SomeButtonClick()
{
    var mgr = new SessionManager();
    var success = await mgr.StartUp("string");

    if (success)
    {
        Console.WriteLine(mgr.SessionId);
        // update ui or whatever
    }
}

注意:此功能在.Net 4.5中可用。

答案 1 :(得分:1)

使用C#功能async并等待您可以将异步流重写为类似于同步流的内容。您只提供了代码的一些片段,以便提供一个完整的示例,我创建了一些类似于您的代码的代码:

class StartEventArgs : EventArgs {

  public StartEventArgs(Int32 sessionId, Int32 errorCode) {
    SessionId = sessionId;
    ErrorCode = errorCode;
  }

  public Int32 SessionId { get; private set; }

  public Int32 ErrorCode { get; private set; }

}

delegate void StartEventHandler(Object sender, StartEventArgs e);

class ServiceProvider {

  public event StartEventHandler Start;

  public void Startup(Boolean succeed) {
    Thread.Sleep(TimeSpan.FromSeconds(1));
    if (succeed)
     OnStart(new StartEventArgs(321, 0));
   else
     OnStart(new StartEventArgs(0, 123));
  }

  protected void OnStart(StartEventArgs e) {
    var handler = Start;
    if (handler != null)
      handler(this, e);
  }

}

ServiceProvider.Startup方法会在触发成功或失败的事件之前延迟一秒,具体取决于所提供的succeed参数。该方法相当愚蠢,但希望与您的ServiceProvider.Startup方法的行为类似。

您可以使用TaskCompletionSource

将异步启动转换为任务
Task<Int32> PerformStartup(ServiceProvider serviceProvider, Boolean succeed) {
  var taskCompletionSource = new TaskCompletionSource<Int32>();
  serviceProvider.Start += (sender, e) => {
    if (e.ErrorCode > 0)
      throw new Exception(e.ErrorCode.ToString());
    taskCompletionSource.SetResult(e.SessionId);
  };
  serviceProvider.Startup(succeed);
  return taskCompletionSource.Task;
}

注意Start事件发出的错误如何转换为Exception(在生产代码中,您应该使用自定义异常类型)。

使用C#的async和await功能,你现在可以编写看起来非常像同步代码的代码,即使它实际上是异步的:

async void Startup(Boolean succeed) {
  var serviceProvider = new ServiceProvider();
  try {
    var sessionId = await PerformStartup(serviceProvider, succeed);
    Console.WriteLine(sessionId);
  }
  catch (Exception ex) {
    Console.WriteLine(ex);
  }
}

如果Start事件报告错误,您现在可以在catch块中处理。会话ID也只是函数的返回值。 “神奇”是在await上使用Task将在任务完成时返回任务的结果,如果在任务中抛出异常,则可以在等待任务的线程上捕获该任务。 / p>