【问题标题】:UWP: how to optimize synchronization between WCF WebServices and SQLite through async callsUWP:如何通过异步调用优化 WCF WebServices 和 SQLite 之间的同步
【发布时间】:2017-04-01 16:32:33
【问题描述】:

我必须在 SQLite 数据库 中同步来自 WCF WebServices 的数据。 这种同步代表了十几个 WebService,可以“分组”为 4 类:

  • 用户的”权利
  • Forms”数据,可从两侧(用户/服务器)更新
  • 服务器”数据,仅从服务器更新
  • Views”数据,在本地复制服务器的视图

对 WebService 的每次调用都是通过 HttpClient 完成的:

response = await client.PostAsync(webServiceName, content);

每个 WebService 都有自己的 async 方法,其中 WebService 响应被反序列化

public static async Task<string> PushForm(List<KeyValuePair<string, string>> parameters)
{
    var response = await JsonParser.GetJsonFromUrl(WebServiceName.PushForm.Value, parameters);
    Forms forms = new Forms();
    try
    {
        forms = JsonConvert.DeserializeObject<Forms>(response);
        return response;
    }
    catch (Exception e)
    {
        throw new Exception(e.Message);
    }
}

然后我有一个 SynchronizationService 类,我在其中按类别重新组合对 WebServices 的调用:

public async Task<bool> SynchronizeServerData()
{
    bool result = false;
    try
     {
        result = true;
        List<Table1> tables1 = await WebServices.GetListTable1(null);
        if (tables1 != null)
        {
            ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table1>(true);
            ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table1>(tables1);
        }
        List<Table2> tables2 = await WebServices.GetListTable2(null);
        if (tables2 != null)
        {
            ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table2>(true);
            ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table2>(tables2);
        }
        List<Table3> tables3 = await WebServices.GetListTable3(null);
        if (tables3 != null)
        {
            ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table3>(true);
            ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table3>(tables3);
        }
        ...
    }
    catch (Exception e)
    {
        result = false;
    }
    return result;
}

最后,在主视图模型中,我调用了以下每个方法:

public async void SynchronizeData(bool firstSync)
{
    IsBusy = true;
    var resUsers = await _synchronisation.SynchronizeUsersRights();
    var resServer = await  _synchronisation.SynchronizeServerData();
    var resForm = await _synchronisation.SynchronizeForms();
    var resViews = await _synchronisation.SynchronizeViews();
    IsBusy = false;
}

但是由于使用了“await”,性能并不好。

=> 我想知道是否有一种简单的方法可以“并行化”调用以优化性能?或者是否可以为此将数据恢复与 SQLite 更新分开?

【问题讨论】:

    标签: c# wcf asynchronous synchronization async-await


    【解决方案1】:

    从表面上看,似乎有机会同时运行一些东西,例如在SynchronizeData 你可以这样做:

    {
        IsBusy = true;
        Task resUsersTask = _synchronisation.SynchronizeUsersRights();
        Task resServerTask = _synchronisation.SynchronizeServerData();
        Task resFormTask = _synchronisation.SynchronizeForms();
        Task resViewsTask = _synchronisation.SynchronizeViews();
        await Task.WhenAll(resUsersTask, resServerTask, resFormTask, resViewsTask);
        var resUsers = resUsersTask.Result;
        var resServer = resServerTask.Result;
        var resForm = resFormsTask.Result;
        var resViews = resViewsTask.Result;       
        IsBusy = false;
    }
    

    ...这将允许这 4 个任务同时运行。

    您可以在SynchronizeServerData 中做同样的事情;例如:

    result = true;
    Task tables1Task = WebServices.GetListTable1(null);
    Task tables2Task = WebServices.GetListTable2(null);
    Task tables3Task = WebServices.GetListTable3(null);
    
    List<Table1> tables1 = await tables1Task;
    // ...
    
    List<Table2> tables2 = await tables2Task;
    // ...
    
    List<Table3> tables3 = await tables3Task;
    // ...
    

    这将允许 3 个任务同时运行。

    您实际从中获得多少可能取决于 SQLite 是否允许您执行多个并发请求 - 我不知道答案。

    其他一些cmets:

    public async void SynchronizeData(bool firstSync)
    

    async void 几乎总是不正确的,除了事件处理程序和罕见的即发即弃方法,通常async Task 是你想要的。在 SO 中搜索任意数量的关于为什么的好的答案。

    最后,你真正应该做的是分析代码,看看你真正的瓶颈在哪里。

    【讨论】:

    • 感谢您的反馈。但是有没有办法通过 Task.WhenAll() 知道异步任务调用的“结果”?如果同步失败,我需要知道这个结果并通知用户。
    • 是的;请参阅Task.WhenAll 上的文档。如果您愿意,也可以先创建任务(如我在 SynchronizeServerData 示例中所示),然后检查每个结果,例如与 task.Result,WhenAll() 完成后。
    • 顺便说一句,因为我最初有点省略了结果检索,我已经调整了答案以反映一种方法。如果你有很多任务,你可能会使用一个集合来代替,但只有 4 个就很简单了。
    • 再次感谢!我还有一个关于在同步期间跨不同级别的调用管理异常的问题。我想通知用户遇到的异常(密码错误、超时、连接丢失……),但在某些情况下,我还必须传递“null”结果以继续同步。有没有合适的方法来做到这一点?
    • 当您使用 await 时,您可以捕获被调用代码在与 await 相同级别抛出的异常。当您使用 Task.WhenAll() 时,您仍然可以访问所有任务中抛出的所有异常,在等待返回结果的任务或通过检查每个任务( .TaskStatus, .Exception) WhenAll() 完成后。我不确定你所说的“传递空结果”是什么意思 - 如果它不是特定于这个问题,你可能应该创建一个新问题以避免我们在这里偏离主题。
    猜你喜欢
    • 1970-01-01
    • 2015-08-03
    • 2014-04-11
    • 1970-01-01
    • 1970-01-01
    • 2014-03-06
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多