小Q的博客

  • 首页
  • net编程
    • 产品和框架
    • 开发实例
    • 经验技巧
    • 开源组件
  • wp独立站
  • 自媒体
  • 日记本
  • 工具箱
每个程序员,都应该有一个自己的博客站
  1. 首页
  2. net编程
  3. 开发实例
  4. 正文

devexpress中grid控件教程 多线程异步加载数据,进度条展示

2022年10月22日 2800点热度 4人点赞 0条评论

devexpress中最强大的控件,要数它的Grid了。几乎任务数据都可以展示,但今天要用它做另一个功能。假设我们开发这样一款软件:视频编辑软件。里面有个功能,提取视频中的音频。一般流程是先要把要提取的视频文件,加载到Grid里,然后点击一个按钮,完成提取操作。今天就用异步+多线程的模式实现它。提取进度在Grid中用进度条展示出来

grid控件

实现之前还是再聊些基础的,同步就是我们正常写的代码,一步一步。如果碰到执行时间较长的操作,整个界面会卡住不能动。于是就出现了异步操作,相当于很多同步操作可以一起做,本身却互不影响,也可以执行界面上其他操作。但异步并不能提高效率,于是就有了多线程。
实现多线程的方式有很多,早期的包括Thread类、BackgroundWorker1组件、Timer组件、Task组件。今天我们就是用Task组件实现的。

测试数据:96个视频文件,从本地文件夹中加载至Grid控件

Table of Contents

Toggle
  • 1、加载数据
    • 1.1、同步加载
    • 1.2、异步加载
    • 1.3、异步+多线程加载
  • 2、执行长任务

1、加载数据

1.1、同步加载

最直接的代码,耗时11s多

//方法1:同步处理
string audioFiles = FormUIHelper.OpenFileDialog(true, this.MediaFileExtList, "视频文件");
if (string.IsNullOrEmpty(audioFiles))
    return;

string[] arrayFile = audioFiles.Split(';');

Stopwatch sw1 = new Stopwatch();
sw1.Restart();

FormUIHelper.ShowWaitForm("提示", "正在加载...");

List<FileInfoBillModel> listFile = new List<FileInfoBillModel>();

foreach (string audioFile in arrayFile)
{
    FileInfoBillModel modelAdd = this.AddFile(audioFile);
    listFile.Add(modelAdd);
}

this.blistFileInfo = FormUIHelper.SetGridDataSource<FileInfoBillModel>(this.gridFile, listFile, null, false);
sw1.Stop();

FormUIHelper.CloseWaitForm();
FormUIHelper.AddLog("共加载文件[" + listFile.Count + "]个,同步耗时:" + sw1.Elapsed.TotalSeconds + "秒", this.fmLog);

1.2、异步加载

异步采用的是async方式,14s多。其实多试几次,会发现和同步用的时间差不多

async Task AddFileAndSetGridDataByAsync()
{
    Stopwatch sw1 = new Stopwatch();
    sw1.Restart();

    FormUIHelper.ShowWaitForm("提示", "正在加载...");

    DataTable dtFile = await AddFileByAsync();
    FormUIHelper.SetGridDataSource(this.gridFile, dtFile);

    sw1.Stop();

    FormUIHelper.CloseWaitForm();
    FormUIHelper.AddLog("共加载文件[" + dtFile.Rows.Count + "]个,异步耗时:" + sw1.Elapsed.TotalSeconds + "秒", this.fmLog);
}
async Task<DataTable> AddFileByAsync()
{
    DataTable dtFile = null;

    string audioFiles = FormUIHelper.OpenFileDialog(true, this.MediaFileExtList, "视频文件");
    if (string.IsNullOrEmpty(audioFiles))
        return dtFile;

    List<FileInfoBillModel> listFile = new List<FileInfoBillModel>();

    await Task.Run(() =>
    {
        string[] arrayFile = audioFiles.Split(';');
        foreach (string audioFile in arrayFile)
        {
            FileInfoBillModel modelAdd = this.AddFile(audioFile);
            listFile.Add(modelAdd);
        }
    });

    dtFile = DataTableHelper.GetTableByList<FileInfoBillModel>(listFile);
    return dtFile;
}

1.3、异步+多线程加载

多线程是基于Task的,6s多就够了。看看效率,几乎提升了一倍。如果数据更多,效果会更明显

//方法3:异步+多线程方法
string audioFiles = FormUIHelper.OpenFileDialog(true, this.MediaFileExtList, "视频文件");
if (string.IsNullOrEmpty(audioFiles))
    return;

List<Task> listTask = new List<Task>();
List<FileInfoBillModel> listFile = new List<FileInfoBillModel>();

Stopwatch sw1 = new Stopwatch();
sw1.Restart();

FormUIHelper.ShowWaitForm("提示", "正在加载...");

string[] arrayAudioFile = audioFiles.Split(';');
foreach (string audioFile in arrayAudioFile)
{
    Action action = () =>
    {
        FileInfoBillModel modelAdd = this.AddFile(audioFile);
        listFile.Add(modelAdd);
    };
    Task task = Task.Factory.StartNew(action);
    listTask.Add(task);
}

Task.Factory.ContinueWhenAll(listTask.ToArray(), completedTasks =>
{
    this.Invoke((EventHandler)delegate
    {
        FormUIHelper.SetGridDataSource<FileInfoBillModel>(this.gridFile, listFile, null, false);                  

        sw1.Stop();
        FormUIHelper.AddLog("共加载视频[" + listTask.Count + "]个,异步+多线程耗时:" + sw1.Elapsed.TotalSeconds + "秒", this.fmLog);

        FormUIHelper.CloseWaitForm();
    });
});

这张图是3种方法的执行时间,对比之下一目了然

三种结果

2、执行长任务

我们以从视频中提取音频为例。准备工作是加载Grid的Model类,要加个属性,名称:Progress、类型:double,用于显示进度。还要设置好最大最小值、是否显示标题和百分比。
多线程依然采用的是Task.Factory方法,代码如下。

List<int> listRowIndex = FormUIHelper.GetGridSelectedRows(this.gridviewFile);
if (listRowIndex.Count <= 0) return;

//批量多线程处理
DateTime beginTime = DateTime.Now;
List<Task> listTask = new List<Task>();

foreach (int rowIndex in listRowIndex)
{
    Action action = () =>
    {
        string targetFile = null;              

        FileInfoBillModel model1 = FormUIHelper.GetGridRowData<FileInfoBillModel>(this.gridviewFile, rowIndex);
        string sourceFile = model1.FullName;
        FileInfo sourceFi = new FileInfo(sourceFile);

        List<string> listP = new List<string>();


        ////提取音频
        ////参考地址:https://haofly.net/ffmpeg/
        ////格式:ffmpeg -i input.mp4 -vn output.mp3
        //listP.Add("-i");
        //listP.Add(@"""" + sourceFile + @"""");
        //listP.Add("-vn");
        //targetFile = sourceFi.DirectoryName + @"\" + Path.GetFileNameWithoutExtension(sourceFile) + ".mp3";
        //listP.Add(@"""" + targetFile + @"""");


        ////去除音频
        ////参考地址:https://haofly.net/ffmpeg/
        ////格式:ffmpeg -i input.mp4 -an output.mp4
        //listP.Add("-i");
        //listP.Add(@"""" + sourceFile + @"""");
        //listP.Add("-an");
        //targetFile = sourceFi.DirectoryName + @"\" + Path.GetFileNameWithoutExtension(sourceFile) + "_noaudio.mp4";
        //listP.Add(@"""" + targetFile + @"""");


        //若目标文件存在,先删除
        if (File.Exists(targetFile))
            File.Delete(targetFile);

        string ps = string.Join(" ", listP);

        //记录开始时间
        DateTime beginTime1 = DateTime.Now;
        model1.BeginTime = beginTime1;
        model1.BeginTimeText = beginTime1.ToString(BaseHelper.TimeFormat);

        string resLog = this.ExecFfmpegByArgs(this.ffmpegFile, ps, rowIndex);           
    };

    Task task = Task.Factory.StartNew(action);
    listTask.Add(task);
}

Task.Factory.ContinueWhenAll(listTask.ToArray(), completedTasks =>
{
    this.Invoke((EventHandler)delegate
    {
        DateTime endTime = DateTime.Now;
        TimeSpan ts2 = endTime - beginTime;

        FormUIHelper.AddLog("任务处理完成,耗时:" + Math.Round(ts2.TotalSeconds, 2) + "秒", this.fmLog);
    });
});

上面还不是最关键的代码,下面ExecFfmpegByArgs这个才是。这个方法是执行提取音频的真正逻辑,基于ffmpeg的。这部分教程在另一篇博客里,有兴趣的可以去看看。关键代码如下所示,根据执行的进度,不断对Progress赋值就可以了

this.Invoke((EventHandler)delegate
{
    //记录任务耗时
    object beginTimeTemp = FormUIHelper.GetGridCellValue(this.gridviewFile, rowIndex, "BeginTime");
    if (!StringHelper.ObjectIsNullOrEmpty(beginTimeTemp))
    {
        DateTime beginTime = DateTime.Parse(beginTimeTemp.ToString());

        TimeSpan ts1 = endTime - beginTime;
        FormUIHelper.SetGridCellValue(this.gridviewFile, rowIndex, "TotalSeconds", Math.Round(ts1.TotalSeconds, 2).ToString() + "秒");
    }

    //回填返回日志
    FormUIHelper.SetGridCellValue(this.gridviewFile, rowIndex, "Content", ps);
    this.gridviewFile.RefreshRow(rowIndex);
});

需要完整代码的

可以加博主微信:xiyang1011

免费获取

标签: devexpress教程 grid控件 多线程 异步 进度条
最后更新:2023年4月3日

小Q

80后中年不油腻大叔,喜欢编写代码、打羽毛球、做木制玩具。目前定居浙江杭州

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2022 小Q的博客. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

浙ICP备2022019157号-2