下面的代码可以使用vba来并发完成吗?

这段代码用一个模板,产生2025年每天的日报表备用。

Sub saleReport()
    t1 = Timer()
    Dim wb As Workbook
    Dim ws As Worksheet
    Dim templatePath As String
    Dim oYear, oMonth, DaysInMonth As Integer
    templatePath = "d:\templateSale.xlsx"
    Set wb = Workbooks.Open(templatePath)
    Set ws = wb.Sheets(1)
    oYera = 2025
    For oMonth = 1 To 12
        DaysInMonth = Day(DateSerial(oYear, oMonth + 1, 1) - 1)
        For oDay = 1 To DaysInMonth
            fn = "2025年" + CStr(oMonth) + "月" + CStr(oDay) + "日的销售报表"
            newFilePath = "d:\" + fn + ".xlsx"
            ws.Name = fn
            wb.SaveAs Filename:=newFilePath, FileFormat:=xlOpenXMLWorkbook
        Next oDay
    Next oMonth
    wb.Close SaveChanges:=False
    Set wb = Nothing
    Set ws = Nothing
    t2 = Timer()
    Debug.Print t2 - t1
End Sub

程序运行了23秒。有无办法使用vba来并行调用我的4核cpu?将速度降低在几秒内?

阅读 482
avatarAI BotBETA

答案

VBA 本身并不直接支持多线程或并发处理。然而,你可以通过一些方法间接实现并行处理,比如使用 Excel 的 Application.OnTime 方法进行任务调度,或者借助外部库如 Windows API 调用。不过,对于 VBA 来说,最常见和相对简单的方法是通过将任务拆分成多个独立的 VBA 宏,并在不同的 Excel 实例中运行这些宏来实现并行处理。

以下是一个概念性的解决方案,涉及使用 Windows 脚本宿主(WSH)和批处理文件来启动多个 Excel 实例,每个实例运行一个修改过的 VBA 宏来处理部分月份的数据。

  1. 拆分 VBA 宏:将原始宏拆分成多个宏,每个宏处理几个月的数据。
  2. 创建批处理文件:编写一个批处理文件,使用 start 命令启动多个 Excel 实例,每个实例打开一个包含相应宏的工作簿并运行它。
  3. 合并结果:在所有宏运行完成后,如果需要,可以编写一个额外的宏或脚本来合并结果。

请注意,这种方法可能需要一些额外的编程工作,并且处理多个 Excel 实例可能会增加内存使用。此外,由于 VBA 和 Excel 的单线程性质,这种方法可能不会完全利用多核 CPU 的所有潜力,但它可以显著减少总运行时间,特别是当处理大量数据时。

对于你的具体代码,你需要将其拆分成多个部分,每个部分处理几个月的数据,并在不同的 Excel 实例中运行。这种方法可以显著减少运行时间,但具体减少多少取决于多种因素,包括系统性能、任务量以及任务之间的依赖性。

如果你不熟悉批处理文件和 Windows 脚本宿主,可能需要一些时间来学习和实现这种方法。另外,请确保在尝试并行处理时考虑到 Excel 的资源限制和潜在的冲突。

2 个回答

Python或者c++这种多线程的语言试试,
用 Python multiprocessing 模块并发:

import openpyxl
from multiprocessing import Pool
import os
import datetime

def create_daily_report(date_tuple):
    year, month, day = date_tuple
    template_path = "d:\\templateSale.xlsx"
    new_file_name = f"{year}年{month}月{day}日的销售报表.xlsx"
    new_file_path = os.path.join("d:\\", new_file_name)

    workbook = openpyxl.load_workbook(template_path)
    worksheet = workbook.active
    worksheet.title = new_file_name
    # ... 根据日期修改工作表 ...

    workbook.save(new_file_path)
    workbook.close()
    print(f"已生成:{new_file_path}")

if __name__ == "__main__":
    year = 2025
    dates = []
    for month in range(1, 13):
        days_in_month = (datetime.date(year, month + 1, 1) - datetime.timedelta(days=1)).day if month < 12 else 31
        for day in range(1, days_in_month + 1):
            dates.append((year, month, day))

    # 4个进程的进程池
    with Pool(processes=4) as pool:
        pool.map(create_daily_report, dates)

    print("所有报表生成完毕!")

你这样的场景,可以使用多线程方式解决:

' 引入必要的Windows API函数声明
Private Declare PtrSafe Function CreateThread Lib "kernel32" ( _
    ByVal lpThreadAttributes As LongPtr, _
    ByVal dwStackSize As Long, _
    ByVal lpStartAddress As LongPtr, _
    lpParameter As Any, _
    ByVal dwCreationFlags As Long, _
    lpThreadId As LongPtr _
) As LongPtr

Private Declare PtrSafe Function CloseHandle Lib "kernel32" ( _
    ByVal hObject As LongPtr _
) As Long

' 定义一个自定义数据类型,用于传递参数给线程函数
Type ThreadParams
    TemplatePath As String
    YearValue As Integer
    MonthValue As Integer
    DayValue As Integer
    NewFilePath As String
End Type
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏