2016-06-08 62 views
1

我有一个有趣的问题..我有一个登录方法工作的WCF服务。C#TaskCompletionSource无法正常工作

我创建了一个taskcompletion并等待结果出现。

好的问题是,如果我调用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

如果你的事件已经被注册了,你基本上只返回不做任何事的Task(不使用tcs变量)。 – Evk

+0

@Evk,谢谢你的回复,但我不知道如何解决它? – unbalanced

+0

很难说只给出了代码,但想到的一件事是将所有TaskCompletionSource存储在列表(字段)中,并在LoginByUserNameCompleted触发时 - 设置_all_任务完成源的结果。 – Evk

回答

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)。