1.问题
最近在做一个由ArcGIS数据格式导入到PostgreslSQL的小工具,数据量比较大(千万级别,见下图),起初为了给使用者一个“比较好”的使用体验,耍了点"小聪明",在导入的界面上增加了两个ProgressBar,一个用于实时显示表的进度,另一个用于实时显示表记录的进度。开发好真正投入使用时,引发了一系列的不满(导入慢、出现未响应假死等等)。
2.解决办法
2.1忽滥用ProgressBar
问题如上述,有一个PorgressBar是用于实时显示表记录进度的,排查了很久才想明白,行记录有多少UI就更新多少次,所以一定程度上影响了入库的效率,好,首先把行记录的进度条给屏蔽,F5(表808个,记录33万),时间缩短了3/4,天啊....
解决办法:
(1)、忽滥用ProgressBar;
(2)、适当增加更新值的间隔
2.2批量提交
如100条提交一次,但这样不利用审计。
if (currentCount % 100 == 0 || currentCount== featureCount)
{
AppConst.postgreHelper.ExecuteNonQuery(connString, CommandType.Text, commitSQL);
commitSQL = "";
}
//AppConst.postgreHelper.ExecuteNonQuery(connString, CommandType.Text, sql);
2.3采用多线程
定义入库任务类:
public class DataImportTask
{
//更新主线程ProgressBar的委托
public delegate void UpdateProgressBar(int step);
public UpdateProgressBar UpdateProgressBarDelegate;
//完成入库任务时通知主线程的委托
public delegate void DataImportFinishTask();
public DataImportFinishTask FinishCallBack;
public void DoWork(object obj)
{
var options = (ImportOptions)obj;
var count = options.CurrentCount;
var interval = options.Interval;
for (int i = 0; i < count; i++)
{
//根据间隔更新主线程ProgressBar进度
if (i % interval == 0)
UpdateProgressBarDelegate(interval);
}
//任务完成时通知主线程作出相应的处理
FinishCallBack();
}
}
定义需要传的参数
public class ImportOptions
{
/// <summary>
/// 进度条更新间隔
/// </summary>
public int Interval { get; set; }
/// <summary>
/// 当前线程入库数量
/// </summary>
public int CurrentCount { get; set; }
}
执行多线程
public partial class FrmMain : Form
{
delegate void AsynUpdateProgressBar(int step);
public FrmMain()
{
InitializeComponent();
}
private void Button1_Click(object sender, EventArgs e)
{
int total = 1000000000;
int taskCount = Convert.ToInt32(textBox1.Text.ToString());
int count = total / taskCount;
var options = new ImportOptions {
CurrentCount = count,
Interval=100000
};
progressBarX1.Maximum = total;
progressBarX1.Minimum = 0;
progressBarX1.Value = 0;
for (int i = 0; i < taskCount; i++)
{
DataImportTask dataTask = new DataImportTask();
dataTask.UpdateProgressBarDelegate += UpdataProgressBarStatus;
dataTask.FinishCallBack += FinishCallBack;
Thread t = new Thread(new ParameterizedThreadStart(dataTask.DoWork));
t.IsBackground = true;
t.Start(options);
}
}
//更新进度条
private void UpdataProgressBarStatus(int step)
{
if (InvokeRequired)
{
this.Invoke(new AsynUpdateProgressBar(delegate (int s)
{
this.progressBarX1.Value += s;
}), step);
}
else
{
this.progressBarX1.Value += step;
}
}
//完成导入任务
private void FinishCallBack()
{
MessageBox.Show("任务完成...");
}
}
以1000000000为例,最终如下图所示: