2015-07-28 51 views
2

我做了一些工作的OAuth,在这里我得到我的刷新令牌通过提供异步API方法(GetRefreshTokenAsync):获取异步结果死锁(尽管设置配置等​​待为false)

public async Task<Tokens> RenewAuthentication() 
{ 
    AppTokenResult token = await OAuth.GetRefreshTokenAsync("clientid", 
     "clientsecret", 
    @"myRefreshToken"); 

    string accessToken = token.AccessToken; 
    string refreshToken = token.RefreshToken; 

    return new Tokens(accessToken, refreshToken); 

} 

如果我把这个就像从一个非异步方法,例如:

public void noAsync() 
{ 
    var r = RenewAuthentication(); 
    var x = r.Result; 
} 

它锁死应用程序:(如果我删除第二行(r.Result),那么它的工作原理,但是这是废话,因为我无法获得。结果我试着读

http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

但通过向GetRefreshTokenAsync()方法添加.ConfigureAwait(false)方法尝试其方法1之后,它没有任何区别。

有什么建议吗?

+0

所以UI应该工作而结果从没有收到? – Thulur

+0

这是在什么平台上运行?控制台,WPF,iOS,ASP.NET ...?你为什么要从同步方法调用异步方法?建议使用从头到尾的异步。 – Krumelur

+0

在收到结果时,UI可以工作或不工作。虽然我宁愿屏蔽用户界面,但这不是真正的问题,对吧?问题是整个事情都陷入僵局。 在WinForms上运行。我从窗体加载调用异步。当表单首次加载时,我想让用户使用OAuth,以便我可以存储它们的令牌。 – Prof

回答

5

它锁死应用程序:(

这是在具有自定义同步将做一个环境的async方法是什么阻挡。

如果调用此的“FormLoad”,你请不要在需要阻止只要选中你的事件处理程序async这样你就可以await上的操作:

public Form() 
{ 
    this.Load += OnLoadHandler; 
} 

public async void OnLoadHandler(object sender, EventArgs e) 
{ 
    var result = await RenewAuthenticationAsync(); 
    // Store the result here 
} 
+0

是的,这将工作,但我很幸运能够参加我可以添加异步限定词的事件。如果我想从主函数调用它,会发生什么? – Prof

+0

你可以调用一个异步方法方法“fire and forget way”。您必须摆脱阻止异步操作的想法。 – Krumelur

+0

然后你会被拧紧。 'async'往往会在你的应用程序中增长并拥有自己的代码库。你不能自然地混合同步和异步方法,这是行不通的。唯一的选择是'async void',这很糟糕,绝对不能使用。你可以设计你的应用程序,以便它可以解决这个问题。如果不能,你可以使用同步API。如果你正在寻找可扩展性,做'async'将会主要受益。如果这不是问题,同步也可以。 –

1

如果您访问任务的结果属性,它将作为一个Future,它会阻塞当前线程,直到任务与结果返回。

我不认为你实际上得到一个僵局,但你在等待永远不会完成任务。

确保您的来电OAuth.GetRefreshTokenAsync回报。

+0

它会返回,如果我删除.Result。如果我在RenewAuthentication中的字符串赋值处断点,那么当我不使用.Result()时它们会被命中。当我添加.Result时,GetRefreshTokenAsync永远不会返回(完成)。我能做什么? – Prof

+0

当然,你是对的。对于GetRefreshTokenAsync的调用显然是由等待任务结果的同一个线程等待的。所以确实,你是对的......一个僵局。 要解决此问题,您必须在单独的任务中等待任务的结果。所以,如果你更换 '变种X = r.Result' 通过 'Task.Run(()=> {VAR X = r.Result; //做一些与X这里 });' 一切都应该工作如预期。 – Norbert

-1

async代码是一路构造。你不能做半个异步,不应该试图强制它。在这里,noAsync似乎没有任何用处。

在他的智慧,克利里先生明确指出,每个async方法都有其自己的上下文中,它可以在用户界面上运行延续。

你可以肯定地说OAuth.GetRefreshTokenAsync是使用ConfigureAwait(false)内部?如果没有,你会陷入僵局。

+0

我不能确定GetFreshTokenAsync内部使用ConfigureAwait(false) – Prof

+0

我设法抓住源代码。它不使用ConfigureAwait(false),但是如果我在公开的RenewAuthenatication()后粘贴.ConfigureAwait,它不会死锁(尽管它不是很有用,因为我无法提取任务结果) – Prof

1

您需要确保RenewAuthentication的结果没有编组回到UI线程。 ConfigureAwait(false)是非常正是为了这样一个目的:

var renewAwaitable = RenewAuthentication().ConfigureAwait(false); 
var result = renewAwaitable.GetAwaiter().GetResult(); 

不过,我会用全力开展与异步,而不是建议。没有任何理由阻止UI应用程序中的任何地方 - 只需禁用在等待令牌返回时不得使用的任何控件,并在启用它时启用它们。

+1

结束了所有的异步。 – Prof