VSTO 开发 EXCEL 委托与多线程的极简示例

VSTO 开发 EXCEL 委托与多线程的极简示例

  • 问题
  • 解决
  • 步骤
  • 代码

问题

这几天做 excel 加载项时遇到一个问题,对话框弹窗显示后,需要等待网络数据的返回来填充 ListBox 控件,由于网络延迟问题,整个窗体连带 Excel 一起白屏卡顿 5-10秒,体验感极差。

解决

这个问题其实在网络编程中是最常见的,通常都是采用 post 异步操作。于是我在 vb 中引用了 Task 异步载入数据,但问题依旧。查阅资料后原来是 vb.net 异步是单线程的,IO 繁忙阻塞了后续代码的执行。在 郑广学 老师的指点下,采用 vb 委托回调的概念,果然解决了这个痛点。

步骤

  1. 打开 Visual Studio 2017/2019/2021
  2. 创建一个 Excel VSTO 外接程序的新项目
  3. 添加一个 功能区(可视化设计器),编译器创建 Ribbon1.vb
  4. 点击工具面板增加一个按钮
  5. 添加一个 Windows 窗体,在窗体里添加 RichText 控件

代码

代码文件 Ribbon1.vb,设置按钮弹窗响应

Imports Microsoft.Office.Tools.Ribbon
Public Class Ribbon1
    Private Sub Button1_Click(sender As Object, e As RibbonControlEventArgs) Handles Button1.Click
        Dim DW As New Form1
        DW.Show()
    End Sub
End Class

代码文件 Form1.vb, 声明委托、多线程、回调

Public Class Form1

    Public Delegate Sub UpdateText(ByVal txt As String)     '定义一个空委托,只有名字和参数
    Private LoadText As UpdateText              '声明一个委托类型变量

    Private Sub TextSet(ByVal txt As String)    '定义函数,将参数 txt 存入 RichText 控件中
        Me.RichTextBox1.Text = txt
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        Dim thId1 As Integer = Threading.Thread.CurrentThread.ManagedThreadId
        LoadText = AddressOf TextSet    '当函数载入内存后,将其指针存到变量 LoadText
        Dim sthread As New Threading.Thread(         '创建第二线程并载入匿名函数
            Sub()
                Dim thId2 As Integer = Threading.Thread.CurrentThread.ManagedThreadId
                Threading.Thread.Sleep(5000)        '模拟网络延迟 5 秒
                Dim res As String = $"当前线程号:{thId2},来自 Web 的问候"     '读到网络数据
                Me.Invoke(LoadText, res)            '在第二线程中执行回调函数
            End Sub
         )
        sthread.Start()
        RichTextBox1.Text = $"当前线程号:{thId1},数据载入中,请稍候..."
    End Sub

End Class

找到原因了,单线程异步也可以避免阻塞,但是我把 Task.Delay() 时间设置的太短,此时载入窗口的主线程任务还没完成,造成阻塞。

Imports System.Threading.Tasks
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        Dim thId1 As Integer = Threading.Thread.CurrentThread.ManagedThreadId
        Dim p As Task = LoadWebTextAsync()
        RichTextBox1.Text = $"当前线程号:{thId1},数据载入中,请稍后..."
    End Sub
    Async Function LoadWebTextAsync() As Task
        Await Task.Delay(5000)
        Dim thId2 As Integer = Threading.Thread.CurrentThread.ManagedThreadId
        RichTextBox1.Text = $"当前线程号:{thId2},来自 Web 的问候!"
    End Function
End Class

使用单线程异步和多线程异步使用原则:IO 密集型,如读取网络数据,此时 CPU 闲置率高,用单线程异步主要不产生阻塞,程序性能更高。多线程异步适合 计算密集型,如处理一百万行数据,此时 CPU 占用率高,启用多核心的 CPU 可以提高程序性能。

你可能感兴趣的:(excel)