C#TaskCompletionSource无法正常工作

时间:2016-06-08 16:36:38

标签: c# wcf taskcompletionsource

我有一个有趣的问题..我有一个可以运行WCF服务的登录方法。

我创建了一个任务完成并等待结果到来。

问题是,如果我调用2次登录方法,第二次不返回任何内容。我把断点和它进入已完成的事件并调用trysetresult但没有任何返回。

这是我的代码

    public Task<User> LoginByUserName(string userName, string password)
    {
        var tcs = new TaskCompletionSource<User>();

        if (!_registeredEventList.Contains ("LoginByUserNameCompleted")) {
            _registeredEventList.Add ("LoginByUserNameCompleted");


            userService.LoginByUserNameCompleted += (object sender, LoginByUserNameCompletedEventArgs args) => {
                if (args.Error != null)
                    tcs.TrySetException (args.Error);
                if (args.Result != null)
                    tcs.TrySetResult (args.Result);
                else
                    tcs.TrySetResult (null);

            };

        }

        userService.LoginByUserNameAsync (userName,password);
        return tcs.Task;
    }

我这样打电话;

var loginResult= await Task.Run(()=>serviceHelper.LoginByUserName(userName,password));

例如,如果用户一次输入错误的登录信息,则在第二次尝试时,不会返回任何内容。

PS:如果事件已经订阅,则_registeredEventList成立。如果是,那么它不再创造。当我删除那部分时,它可以工作。

2 个答案:

答案 0 :(得分:1)

正如Evk评论的那样,问题是你的代码有一个条件,它永远不会返回完成的任务。具体来说,第一次调用此代码时,它会向_registeredEventList添加一个条目(可能永远不会被删除)。以后的所有调用都将返回一个永不完成的Taskwhich is a major no-no in asynchronous programming

要解决此问题,我建议您通过取消订阅作为回调的一部分来修改您的EAP包装:

public static Task<User> LoginByUserNameTaskAsync(this UserService @this, string userName, string password)
{
  var tcs = new TaskCompletionSource<User>();
  LoginByUserNameCompletedDelegate callback = null;
  callback = (object sender, LoginByUserNameCompletedEventArgs args) =>
  {
    @this.LoginByUserNameCompleted -= callback;
    if (args.Error != null)
      tcs.TrySetException(args.Error);
    else
      tcs.TrySetResult(args.Result);
  };
  @this.LoginByUserNameCompleted += callback;

  @this.LoginByUserNameAsync(userName, password);
  return tcs.Task;
}

(我也把它作为一种扩展方法,让它follow the TAP naming parameters for TAP-over-EAP wrappers)。

答案 1 :(得分:-1)

我刚刚解决了同样的问题。对我来说,它不是继承 IDisposable 并添加以下内容

public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed && disposing)
        {
            _channel?.Dispose();
            _connection?.Dispose();
        }

        this.disposed = true;
    }