【发布时间】:2016-07-29 08:13:55
【问题描述】:
我的 Xamarin Android 应用程序出现问题,但我相信我已将问题范围缩小到 Azure 移动客户端或我对 .Net 中的任务缺乏了解。我试图在控制台应用程序上重新创建我的问题。
我的目标是并行进行多个 api 调用。在下面的示例中,我只是重复调用以检索数据库中确实存在的用户。当我等待每个调用(GetUsersB 方法)时,一切运行正常,但是当我尝试等待 Task.WhenAll(GetUsersA 方法)时,我几乎总是遇到异常。
class Program
{
static void Main(string[] args)
{
var service = new MyService();
try
{
//GetUsersA(service).Wait(); //Often throws the exception attached but ocasionally is successful
GetUsersB(service).Wait(); //Never throws an exception
}
catch (AggregateException e)
{
foreach (var ex in e.InnerExceptions)
{
Console.WriteLine(e.InnerException.Message);
Console.WriteLine(e.InnerException.StackTrace);
}
}
Console.WriteLine("main done");
Console.ReadLine();
}
public async static Task GetUsersA(MyService service)
{
await Task.WhenAll(service.GetUser("d48977fce3c6fc6b5a74c"),
service.GetUser("d48977fce3c6fc6b5a74c"),
service.GetUser("d48977fce3c6fc6b5a74c"),
service.GetUser("d48977fce3c6fc6b5a74c"),
service.GetUser("d48977fce3c6fc6b5a74c"),
service.GetUser("d48977fce3c6fc6b5a74c"));
Console.WriteLine("tasks complete");
}
public async static Task GetUsersB(MyService service)
{
await service.GetUser("d48977fce3c6fc6b5a74c");
await service.GetUser("d48977fce3c6fc6b5a74c");
await service.GetUser("d48977fce3c6fc6b5a74c");
await service.GetUser("d48977fce3c6fc6b5a74c");
await service.GetUser("d48977fce3c6fc6b5a74c");
Console.WriteLine("tasks complete");
}
}
如果需要这里是 MyService 类
public class MyService
{
private MobileServiceClient azClient;
public MyService()
{
azClient = new MobileServiceClient("https://mysite.azurewebsites.net/");
}
public async Task<User> GetUser(string id)
{
return await azClient.GetTable<User>().LookupAsync(id);
}
}
这是内部异常的输出:
Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Enumerator.MoveNext()
at Microsoft.WindowsAzure.MobileServices.MobileServiceContractResolver.CreateProperties(Type type, MemberSerialization memberSerialization)
at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract(Type objectType)
at Microsoft.WindowsAzure.MobileServices.MobileServiceContractResolver.CreateObjectContract(Type type)
at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateContract(Type objectType)
at Newtonsoft.Json.Serialization.DefaultContractResolver.ResolveContract(Type type)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.Linq.JToken.ToObject(Type objectType, JsonSerializer jsonSerializer)
at Newtonsoft.Json.Linq.JToken.ToObject[T](JsonSerializer jsonSerializer)
at Microsoft.WindowsAzure.MobileServices.MobileServiceSerializer.<>c__DisplayClass35_0`1.<Deserialize>b__0()
at Microsoft.WindowsAzure.MobileServices.MobileServiceSerializer.TransformSerializationException[T](Action action, JToken token)
at Microsoft.WindowsAzure.MobileServices.MobileServiceSerializer.Deserialize[T](JToken json, JsonSerializer jsonSerializer)
at Microsoft.WindowsAzure.MobileServices.MobileServiceSerializer.Deserialize[T](JToken json)
at Microsoft.WindowsAzure.MobileServices.MobileServiceTable`1.<LookupAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.WindowsAzure.MobileServices.MobileServiceTable`1.<LookupAsync>d__13.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at TaskTest.MyService.<GetUser>d__2.MoveNext() in C:\Users\jalley\Documents\Visual Studio 2015\Projects\TaskTest\TaskTest\MyService.cs:line 24
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at TaskTest.Program.<GetUsersA>d__1.MoveNext() in C:\Users\jalley\Documents\Visual Studio 2015\Projects\TaskTest\TaskTest\Program.cs:line 31
【问题讨论】:
-
@Prashant 这与他的问题无关。
-
你不应该使用Task.Wait(),因为它会导致死锁。使用 await 或 WhenAll。
-
我已经提交了这个 GitHub 问题,其中包括一个潜在的解决方法:github.com/Azure/azure-mobile-apps-net-client/issues/186
-
@lindydonna-msft 非常感谢您的快速响应、解决方法和解决方案。
标签: c# parallel-processing task-parallel-library azure-mobile-services