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
免费获取


文章评论