C# 从UI线程通过异步任务调用web服务会导致死锁
我是Xamarin表单的新手,有一个应用程序可以异步获取用户的事务,但在调用web服务时遇到了死锁 我有一个TransactionView: 这是XAML:C# 从UI线程通过异步任务调用web服务会导致死锁,c#,xamarin,async-await,deadlock,xamarin.forms,C#,Xamarin,Async Await,Deadlock,Xamarin.forms,我是Xamarin表单的新手,有一个应用程序可以异步获取用户的事务,但在调用web服务时遇到了死锁 我有一个TransactionView: 这是XAML: <Label Text="Account:" Grid.Row="0" Grid.Column="0" Style="{StaticResource LabelStyle}"/> <ctrls:BindablePicker x:Name="ddlAccountsWinPhone" ItemsSource="{Bindi
<Label Text="Account:" Grid.Row="0" Grid.Column="0" Style="{StaticResource LabelStyle}"/>
<ctrls:BindablePicker x:Name="ddlAccountsWinPhone" ItemsSource="{Binding Accounts}" SelectedIndex="{Binding AccountID}" Grid.Row="0" Grid.Column="1" />
<Label Text="From Date:" Grid.Row="2" Grid.Column="0" Style="{StaticResource LabelStyle}"/> <DatePicker x:Name="dtFromWinPhone" Date="{Binding FromDate}" Grid.Row="2" Grid.Column="1" />
<Label Text="To Date:" Grid.Row="3" Grid.Column="0" Style="{StaticResource LabelStyle}"/> <DatePicker x:Name="dtToWinPhone" Date="{Binding ToDate}" Grid.Row="3" Grid.Column="1" />
<Button x:Name="btnViewWinPhone" Text="View" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2"
Command ="{Binding ShowTransactions}" />
以下是TransactionViewModel:
public class TransactionViewModel : ViewModelBase
{
private int accountID;
private List<Account> accounts = new List<Account>();
private Account accountItemSelected;
private DateTime fromDate = new DateTime(1900,1,1);
private DateTime toDate = new DateTime(1900, 1, 1);
private ContentPage transactionGridViewNav;
public TransactionViewModel(INavigation navigationInterface, ContentPage transactionSubPageView)
{
base.navigation = navigationInterface;
transactionSubPageViewNav = transactionSubPageView; //I SAVE A REFERENCE TO THE SUBPAGE HERE
accounts.AddRange(Client.Accounts);
}
public ICommand ShowTransactions
{
get
{
return new Command(async () =>
{
//HERE IS WHERE "I THINK" I WANT TO FETCH THE TRANSACTIONS
//THEN NAVIGATE TO THE THE SUBPAGE WITH THE GRID OF TRANSACTIONS
await navigation.PushAsync(transactionSubPageViewNav);
});
}
}
public int AccountID
{
get{ return this.accountID; }
set{
this.accountID = value;
this.OnPropertyChanged("AccountID");
}
}
public List<Account> Accounts
{
get{
return this.accounts;}
set{
this.accounts = value;
this.OnPropertyChanged("Accounts");
}
}
public Account AccountItemSelected
{
get{return accountItemSelected;}
set {
if (accountItemSelected != value)
{
accountItemSelected = value;
OnPropertyChanged("AccountItemSelected");
}
}
}
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }}
...
public class TransactionSubPageViewModel : ViewModelBase
{
public List<Transaction> transactionsGrid = new List<Transaction>();
public int accountId = 1636;
public DateTime fromDate = new DateTime(2015, 8, 1);
public DateTime toDate = new DateTime(2015, 9, 1);
public TransactionGridViewModel() { }
public List<Transaction> TransactionsGrid
{
get {
if (transactionsGrid == null) transactionsGrid = MyWebService.GetTransactions(1636, new DateTime(2015, 8, 1), new DateTime(2015, 9, 1)).Result;
return transactionsGrid;}
}
}
这是子页面ViewModel:
public class TransactionViewModel : ViewModelBase
{
private int accountID;
private List<Account> accounts = new List<Account>();
private Account accountItemSelected;
private DateTime fromDate = new DateTime(1900,1,1);
private DateTime toDate = new DateTime(1900, 1, 1);
private ContentPage transactionGridViewNav;
public TransactionViewModel(INavigation navigationInterface, ContentPage transactionSubPageView)
{
base.navigation = navigationInterface;
transactionSubPageViewNav = transactionSubPageView; //I SAVE A REFERENCE TO THE SUBPAGE HERE
accounts.AddRange(Client.Accounts);
}
public ICommand ShowTransactions
{
get
{
return new Command(async () =>
{
//HERE IS WHERE "I THINK" I WANT TO FETCH THE TRANSACTIONS
//THEN NAVIGATE TO THE THE SUBPAGE WITH THE GRID OF TRANSACTIONS
await navigation.PushAsync(transactionSubPageViewNav);
});
}
}
public int AccountID
{
get{ return this.accountID; }
set{
this.accountID = value;
this.OnPropertyChanged("AccountID");
}
}
public List<Account> Accounts
{
get{
return this.accounts;}
set{
this.accounts = value;
this.OnPropertyChanged("Accounts");
}
}
public Account AccountItemSelected
{
get{return accountItemSelected;}
set {
if (accountItemSelected != value)
{
accountItemSelected = value;
OnPropertyChanged("AccountItemSelected");
}
}
}
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }}
...
public class TransactionSubPageViewModel : ViewModelBase
{
public List<Transaction> transactionsGrid = new List<Transaction>();
public int accountId = 1636;
public DateTime fromDate = new DateTime(2015, 8, 1);
public DateTime toDate = new DateTime(2015, 9, 1);
public TransactionGridViewModel() { }
public List<Transaction> TransactionsGrid
{
get {
if (transactionsGrid == null) transactionsGrid = MyWebService.GetTransactions(1636, new DateTime(2015, 8, 1), new DateTime(2015, 9, 1)).Result;
return transactionsGrid;}
}
}
如果我从子页面的OnAppearing
事件调用GetTransactions
webservice,它会挂起,如果我从ICommand ShowTransactions调用它,它也会挂起。我是否缺少等待
或继续
我和一些人在一起,我知道我是,但我只是不知道如何解决它
我尝试了configurewait(false)
但运气不佳。如果我能在页面中的a上调用Web服务,那就太理想了。成功!!我知道这将是一个在这里询问的案例,然后我会解决它。这真有趣
这就是我如何让它工作的。请随意评论它:
我将TransactionSubPageViewModel的TransactionGrid
属性转换为异步方法,并将webservice调用直接放在其中:
public async Task<List<Transaction>> TransactionsGrid()
{
if (transactionsGrid == null)
{
var client = new System.Net.Http.HttpClient(new ModernHttpClient.NativeMessageHandler());
client.BaseAddress = new Uri("http://theWebAddress.com/);
var response = await client.GetAsync("API.svc/Transactions/" + accountId + "/" + fromDate.ToString("yyyy-MM-dd") + "/" + toDate.ToString("yyyy-MM-dd"));
var transactionJson = await response.Content.ReadAsStringAsync(); //DONT USE Result HERE, USE AWAIT AS PER @Noseratio suggested
var transactions = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Transaction>>(transactionJson);
transactionsGrid = transactions;
}
return transactionsGrid;
}
然后,当我从OnAppearing()调用PopulateGridTransactions时,数据已经可用:
public void PopulateGridTransactions()
{
...
foreach (Models.Transaction transaction in transactionSubPageViewModel.TransactionsGrid().Result)
{
编辑:
正如@Noseratio所指出的,让我解释一下为什么需要在异步中进行Try/Catch
巧合的是,在从web服务获得响应后,我在下一行反序列化json web服务结果时出错
我在Xamarin App.cs中设置了一个全局异常处理程序,以便它能够捕获它:
通过在异步中放置Try/Catch,我可以捕获同一帧上的异常(当应用程序捕获未处理的异常时,在堆栈展开之前,即):
OT,当您将async void
lambda传递到new命令(async()=>{…})
中时,您应该在其中执行一些异常处理。它们不会在同一堆栈帧上抛出(与非异步lambda一样)。此外,response.Content.ReadAsStringAsync().Result
可能会导致另一个死锁。将其更改为等待响应.Content.ReadAsStringAsync()
。在别处查找.Result
或.Wait()
。不,将try/catch
放入async()
lambda中。试试这个:Try{Action lambda=async()=>{throw new ApplicationException();}}}catch{Debug.Assert(false);}
。你永远无法到达终点。更多。谢谢你,我真的很感激这两条建议!干杯:)我尽力向下一个需要理解这一点的人解释。
public async Task<List<Transaction>> TransactionsGrid()
{
if (transactionsGrid == null)
{
var client = new System.Net.Http.HttpClient(new ModernHttpClient.NativeMessageHandler());
client.BaseAddress = new Uri("http://theWebAddress.com/);
var response = await client.GetAsync("API.svc/Transactions/" + accountId + "/" + fromDate.ToString("yyyy-MM-dd") + "/" + toDate.ToString("yyyy-MM-dd"));
var transactionJson = await response.Content.ReadAsStringAsync(); //DONT USE Result HERE, USE AWAIT AS PER @Noseratio suggested
var transactions = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Transaction>>(transactionJson);
transactionsGrid = transactions;
}
return transactionsGrid;
}
public ICommand ShowTransactions
{
get
{
return new Command(async () =>
{
//THIS IS WHERE I WAS MISSING THE AWAIT!!!
await ((TransactionGridViewModel)transactionSubPageViewNav.BindingContext).TransactionsGrid();
await navigation.PushAsync(transactionSubPageViewNav);
});
}
public void PopulateGridTransactions()
{
...
foreach (Models.Transaction transaction in transactionSubPageViewModel.TransactionsGrid().Result)
{