devexpress中最强大的控件,要数它的Grid了。几乎任务数据都可以展示,但今天要用它做另一个功能。假设我们开发这样一款软件:视频编辑软件。里面有个功能,提取视频中的音频。一般流程是先要把要提取的视频文件,加载到Grid里,然后点击一个按钮,完成提取操作。今天就用异步+多线程的模式实现它。提取进度在Grid中用进度条展示出来
实现之前还是再聊些基础的,同步就是我们正常写的代码,一步一步。如果碰到执行时间较长的操作,整个界面会卡住不能动。于是就出现了异步操作,相当于很多同步操作可以一起做,本身却互不影响,也可以执行界面上其他操作。但异步并不能提高效率,于是就有了多线程。
实现多线程的方式有很多,早期的包括Thread类、BackgroundWorker1组件、Timer组件、Task组件。今天我们就是用Task组件实现的。
测试数据:96个视频文件,从本地文件夹中加载至Grid控件
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
免费获取
文章评论