C# 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 содержит, если событие уже подписано или нет. Если да, то он больше не создается. Когда я удаляю эту часть, она работает.
1 ответ
Как прокомментировал Эвк, проблема в том, что у вашего кода есть условие, при котором он никогда не вернет выполненное задание. В частности, при первом вызове этого кода он добавит запись в _registeredEventList
(который, вероятно, никогда не удаляется). Все последующие вызовы вернут Task
это никогда не завершается, что является основным нет-нет в асинхронном программировании.
Чтобы это исправить, я рекомендую изменить оболочку 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;
}
(Я также сделал это методом расширения и следил за параметрами именования TAP для оболочек TAP-over-EAP).
Я только что решил ту же проблему. Для меня это не наследование 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;
}