C# 与Task.WhenAll并行使用Azure移动客户端拨打电话时出现问题

C# 与Task.WhenAll并行使用Azure移动客户端拨打电话时出现问题,c#,parallel-processing,task-parallel-library,azure-mobile-services,C#,Parallel Processing,Task Parallel Library,Azure Mobile Services,我的Xamarin Android应用程序有问题,但我相信我已经将问题缩小到Azure移动客户端或者我对.Net中的任务缺乏了解。我已尝试在控制台应用程序上重新创建问题 我的目标是并行进行多个api调用。在下面的示例中,我只是重复调用以检索数据库中确实存在的用户。当我等待每次调用(GetUsersB方法)时,一切都正常运行,但当我尝试等待Task.whall(GetUsersA方法)时,我几乎总是遇到异常 class Program { static void Main(st

我的Xamarin Android应用程序有问题,但我相信我已经将问题缩小到Azure移动客户端或者我对.Net中的任务缺乏了解。我已尝试在控制台应用程序上重新创建问题

我的目标是并行进行多个api调用。在下面的示例中,我只是重复调用以检索数据库中确实存在的用户。当我等待每次调用(GetUsersB方法)时,一切都正常运行,但当我尝试等待Task.whall(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);
    }
}
公共类MyService
{
私人移动服务客户机;
公共MyService()
{           
azClient=新的MobileServiceClient(“https://mysite.azurewebsites.net/");           
}
公共异步任务GetUser(字符串id)
{
返回wait azClient.GetTable().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
集合被修改;枚举操作不能执行。
在System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource资源)处
位于System.Collections.Generic.Dictionary`2.Enumerator.MoveNext()
位于Microsoft.WindowsAzure.MobileServices.MobileServiceContractResolver.CreateProperties(类型类型,MemberSerialization MemberSerialization)
位于Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract(类型objectType)
位于Microsoft.WindowsAzure.MobileServices.MobileServiceContractResolver.CreateObjectContract(类型)
位于Newtonsoft.Json.Serialization.DefaultContractResolver.CreateContract(类型objectType)
位于Newtonsoft.Json.Serialization.DefaultContractResolver.ResolveContract(类型)
在Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader阅读器,类型objectType,Boolean checkAdditionalContent)
位于Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader,类型objectType)
位于Newtonsoft.Json.Linq.JToken.ToObject(类型objectType,JsonSerializer-JsonSerializer)
在Newtonsoft.Json.Linq.JToken.ToObject[T](JsonSerializer JsonSerializer)
在Microsoft.WindowsAzure.MobileServices.MobileServiceSerializer.c__DisplayClass35_0`1.b__0()上
在Microsoft.WindowsAzure.MobileServices.MobileServiceSerializer.TransformSerializationException[T](操作,JToken令牌)
在Microsoft.WindowsAzure.MobileServices.MobileServiceSerializer.Deserialize[T](JToken json,JsonSerializer JsonSerializer)
在Microsoft.WindowsAzure.MobileServices.MobileServiceSerializer.Deserialize[T](JToken json)
在Microsoft.WindowsAzure.MobileServices.MobileServiceTable`1.d_u14.MoveNext()上
---来自引发异常的上一个位置的堆栈结束跟踪---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)
在System.Runtime.CompilerServices.TaskWaiter.HandleNonSuccessAndDebuggerNotification(任务任务)中
在Microsoft.WindowsAzure.MobileServices.MobileServiceTable`1.d_u13.MoveNext()上
---来自引发异常的上一个位置的堆栈结束跟踪---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)
在System.Runtime.CompilerServices.TaskWaiter.HandleNonSuccessAndDebuggerNotification(任务任务)中
在System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()中
在C:\Users\jalley\Documents\Visual Studio 2015\Projects\TaskTest\TaskTest\MyService.cs中的TaskTest.MyService.d\uu 2.MoveNext()处:第24行
---来自引发异常的上一个位置的堆栈结束跟踪---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)
在System.Runtime.CompilerServices.TaskWaiter.HandleNonSuccessAndDebuggerNotification(任务任务)中
在System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()中
在C:\Users\jalley\Documents\Visual Studio 2015\Projects\TaskTest\TaskTest\Program.cs中的TaskTest.Program.d\uu 1.MoveNext()处:第31行
查看(第307行),您可以很容易地看到这是
MobileServiceTable的内部设计问题,它似乎不是线程安全的(我不熟悉Xamarin)。因为您使用的是控制台应用程序,所以所有任务都被卸载到线程池,并行可能(也将)发生在第307行。从这一点开始,您就有多个线程在迭代同一个字典,而其他线程在修改它。如果您再看一看,您将看到一些序列化使用“id”属性作为内部使用的缓存键,它可能与代码中的“D489777FCE3C6FC6B5A74C”键相同

这不是一个完整的答案,因为您的代码似乎很好。我不认为这里有“真正的答案”,因为您的代码是完全有效的,即使您可能以一种非常不成熟的方式使用async/await。我只是想你可能想知道为什么会这样

然而

此库可能是用于单线程上下文(如ASP.NET或UI应用程序)之上,而不是控制台应用程序。

(发布新答案,因为这是一个已修复的错误)

您编写的客户机代码应该可以运行,但事实证明,它实际上是移动应用程序客户机SDK中竞争条件的完美测试用例,而竞争条件一直很难重新编写。请在此处查看错误:


这个bug已经被修复,但是一个新的NuGet软件包还没有发布。(我会在有答案时更新此答案。)同时,您可以从源代码构建自己的包以取消阻止您的场景。

检查此链接。@Prashant这与他的问题无关。您不应该使用Task.Wait(),因为它可能导致死锁。使用wait或WhenAll。我已经提交了这个GitHub问题,其中包括一个潜在的解决方法:@lindydonna msft非常感谢您的快速响应、解决方法以及it解决方案。这与答案非常接近,所以我将继续讨论。MobileServiceClient执行一些内部繁重的工作。它的设计目的是