【问题标题】:JsonConvert.DeserializeObject blocks UI threadJsonConvert.DeserializeObject 阻塞 UI 线程
【发布时间】:2016-02-15 12:54:46
【问题描述】:

以下 VB.NET 代码使用 Newtonsoft JSON.NET 库并在按钮单击事件处理程序中执行并阻塞 UI 线程:

Dim table As DataTable = 
Await Task.Factory.StartNew(Function() JsonConvert.DeserializeObject(of DataTable)(result))

我尝试使用不同的语法(Task.Run 等),但它仍然会阻塞 UI 线程。这里使用的正确语法是什么?

编辑:事实证明,当为 DataGridView UI 组件设置 DataSource 时,UI 实际上是阻塞的。 DataTable 中只有 500 条记录用于填充 DataGridView。为什么执行此任务时 UI 会阻塞,即

Dim dv As DataView = table.DefaultView
DataGridView1.DataSource = dv

谢谢

【问题讨论】:

  • 这似乎是正确的,是否有比调用中可能导致阻塞的代码更多的代码?
  • @DoomVroom 没有其他代码可能导致阻塞,但是运行 Windows 窗体的代码在大型 Windows 应用程序内运行的插件类中执行 - 可能是客户端应用程序以某种方式阻止插件类创建新线程?我将如何检测呢?谢谢!
  • 根据我的 JsonConvert 经验,它不需要很长时间。我不知道您要转换的表格有多大。我可能会模拟一个反序列化您的数据表的测试。只需对表和反序列化进行单元测试,看看需要多长时间。看看测试是否符合您的期望。我还假设您也在函数中使用 Async,因为您的代码没有说明它。
  • 您正在通过调用WaitResult 阻止外部任务。
  • @DoomVroom 谢谢-查看我的更新。为 DataGridView 设置数据源时,UI 似乎挂起。关于如何解决这个问题的任何建议?

标签: .net vb.net multithreading asynchronous json.net


【解决方案1】:

我会尝试从您的事件处理程序中删除 Async 关键字,并使用 ContinueWith 方法,看看是否有帮助。 示例:

Dim context = TaskScheduler.FromCurrentSynchronizationContext()
Dim t = Task.Factory.StartNew(Function() JsonConvert.DeserializeObject(Of DataTable)(result))

t.ContinueWith(Sub(res)
    If res.Result IsNot Nothing Then
        ' Do something with your data table
    End If
End Sub, context)

这不会阻塞你的UI线程,操作完成后你就可以处理数据表了。但请记住,您的事件处理程序将立即返回,ContinueWith 处理程序将在稍后触发。所以你可能需要相应地调整你的用户界面(禁用按钮等)

编辑: 更新了代码示例以反映 DoomVroom 的建议

更新: 为了响应 OP 的更新,我建议创建一个视图,并首先在其中加载几条记录。当用户滚动或翻页时,向其中添加更多记录。一次添加 500 多条记录会使 UI 线程不堪重负,您会遇到阻塞。

【讨论】:

  • 执行此方法应该可行,但如果您想做 UI 工作,请确保使用 UI 线程中传递的重载。
  • 谢谢,我更新了我的例子。你是对的,在不传入 UI 线程的情况下访问 UI 线程上的控件会引发异常。
  • 非常感谢您的建议。在设置 gridview 的数据源时,这仍然会阻塞 UI 线程大约 8 秒。
  • 是的,因为您的更新表明在您反序列化之后发生了阻塞,并且当您将数据源设置为 gridview 本身时,这实际上并没有太大帮助。
  • 我现在唯一能建议的就是限制您设置为 gridview 的行数。您可以通过创建“视图”并在您滚动或翻阅网格视图时将记录加载到其中来实现某种虚拟分页来做到这一点。
【解决方案2】:

试试这个:

Dim table As DataTable = 
    Await Task.Run(
        Function() JsonConvert.DeserializeObject(of DataTable)(result))

Dim si As ISupportInitialize = DataGridView1
si.BeginInit()
DataGridView1.DataSource = table.DefaultView
si.EndInit()

【讨论】:

  • 谢谢!我已经尝试过了,但是在设置数据源时它仍然会阻塞大约 8 秒。
  • 尝试禁用网格视图。或者批量添加到网格视图中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-17
  • 1970-01-01
相关资源
最近更新 更多