【问题标题】:C# TaskCompletionSource not workingC# TaskCompletionSource 不工作
【发布时间】:2016-06-08 16:36:38
【问题描述】:

我有一个有趣的问题。我有一个登录方法,它适用于 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 持有是否已经订阅了事件。如果是,则它不会再次创建。当我删除该部分时,它可以工作。

【问题讨论】:

  • 如果您的事件已经注册,您基本上只是返回什么都不做的任务(不使用 tcs 变量)。
  • @Evk,谢谢你的回复,但我不知道如何解决?
  • 很难说只提供了代码,但想到的一件事是将所有 TaskCompletionSource 存储在列表(字段)中,并且当 LoginByUserNameCompleted 触发时 - 设置 all 任务的结果完成源。
  • @Evk,我不确定它是否能解决问题,因为事件被触发并且 TrySetResult 有效..但它没有返回。我想我会取消订阅活动并再次订阅,这似乎是目前的一种解决方案

标签: c# wcf taskcompletionsource


【解决方案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)。

【讨论】:

    【解决方案2】:

    我刚刚解决了同样的问题。对我来说,它没有继承 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;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-23
      相关资源
      最近更新 更多