项目需要,几十万张照片需要计算出每个照片的特征值(调用C++编写的DLL)。

业务流程:选择照片文件夹,分别访问照片-->调用DLL接口传递照片路径-->接收处理返回值-->写入数据库。

前期使用的for循环来处理,几十万张照片处理起来差不多10个小时。速度太慢,后面改进使用Parallel来进行平行计算(调用DLL处理照片),统一写入Datatable,然后使用BulkInsert批量把Datatable写入数据库,目前测试8万张照片并行计算速度30分钟,速度提高约30%-40%左右。

代码示例如下:

private static SqlConnection sqlconn;
private static ConcurrentDictionary<string, int> currInts = new ConcurrentDictionary<string, int>();
private void Button1_Click(object sender, EventArgs e)
{
var dirPath = "";
using (var folderBrowser = new FolderBrowserDialog())
{
if (folderBrowser.ShowDialog() != DialogResult.OK) return;
dirPath = folderBrowser.SelectedPath;
if (!Directory.Exists(dirPath))
{
MessageBox.Show(@"所选路径不存在或无权访问", @"错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
} BeginInvoke(new Action(async () =>
{
button1.Enabled = false;
var sw = new Stopwatch();
sw.Start(); //检测服务器链接
Log.WriteLine(@"尝试连接数据库服务器"); sqlconn = new SqlConnection(
$"Data Source={txt_serverIP.Text},{txt_ServerPort.Text};User ID={txt_User.Text};Password={txt_Pwd.Text};Initial Catalog={txt_DB.Text};Persist Security Info=False;Pooling=true;Min Pool Size=30;Max Pool Size=200;");
if (sqlconn.State == ConnectionState.Closed)
{
try
{
sqlconn.Open();
}
catch (Exception exception)
{
Log.WriteLine($@"连接数据库服务器【失败】-->{exception.Message}");
button1.Enabled = true;
return;
}
} Log.WriteLine($@"连接数据库服务器【成功】{Environment.NewLine}获取未转换图片数据。。。");
var ds = new DataSet();
int.TryParse(txt_start.Text, out var start);
int.TryParse(txt_end.Text, out var end);
var sqlstrALL = "";
if (start == 0 || end == 0)
{
sqlstrALL = "SELECT * FROM ViewWeiZhuanHuan";
}
else
{
sqlstrALL = $"SELECT * FROM ViewWeiZhuanHuan WHERE {txt_mastKey.Text} BETWEEN {start} AND {end}";
} var sqlcmd = new SqlCommand(sqlstrALL, sqlconn);
DataAdapter da = new SqlDataAdapter(sqlcmd);
da.Fill(ds);
if (ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0)
{
Log.WriteLine("所有图片都已经转换完毕。");
sqlconn.Close();
return;
} Log.WriteLine($"{ds.Tables[0].Rows.Count}个图片需要转换。"); var total = ds.Tables[0].Rows.Count;
var rowkey = comboBox1.SelectedValue.ToString();
var splitkey = txt_split.Text.Trim(); #region 定义数据保存
var dt = new DataTable();
dt.Columns.Add("zd1", typeof(int));
dt.Columns.Add("zd2", typeof(int));
dt.Columns.Add("zd3", typeof(string));
dt.Columns.Add("zd4", typeof(string));
dt.Columns.Add("zd5", typeof(string));
dt.Columns.Add("zd6", typeof(string));
#endregion #region 并行执行
currInts.TryAdd("currInts", 1);//初始化进度数字为1
await Task.Run(() =>
{
//使用8个CPU核心来运行
var result = Parallel.For(0, ds.Tables[0].Rows.Count, new ParallelOptions { MaxDegreeOfParallelism = 8}, (rotIndex, state) =>
{
BeginInvoke(new Action(() =>
{
currInts.TryGetValue("currInts", out var currValue);
lb_process.Text = $@"{currValue}/{total}";//显示进度
var nextValue = currValue + 1;
currInts.TryUpdate("currInts", nextValue, currValue);//加1
})); var fileDirPath = "";//根据选择的文件名格式,用填写的规则生成路径 var file = new List<string>{
$"{dirPath}\\{fileDirPath}\\{ksno}_fp1.jpg",
$"{dirPath}\\{fileDirPath}\\{ksno}_fp2.jpg",
$"{dirPath}\\{fileDirPath}\\{ksno}_fp3.jpg"}; foreach (var zwzp in file)
{
try
{
var model = ZwHelper.zwzhAsync($"{zwzp}").Result;//调用C++转换
if (model != null)
{
//并行计算下写入Datatable需要锁定才可以,否则会提示datatable索引损坏
lock (dt.Rows.SyncRoot)
{
var dr = dt.NewRow();
dr["zd1"] = Convert.ToInt32(filexe);
dr["zd2"] = Convert.ToInt32(ds.Tables[0].Rows[rotIndex]["zd1"]);
dr["zd3"] = model.zhtz;
dr["zd4"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
dr["zd5"] = "";
dr["zd6"] = "";
dt.Rows.Add(dr);
} }
else
{
Log.WriteLine($@"{ksno}转换失败");
Log.Log.Error($"{ksno}转换失败。");
}
}
catch (Exception exception)
{
Log.Log.Error($"学号{ksno},图片路径{zwzp}转换失败。{exception}");
}
} });
sw.Stop();
Log.WriteLine($"转换耗时:{sw.ElapsedMilliseconds}毫秒");
Log.WriteLine($@"开始写入数据库,数量{dt.Rows.Count}"); #region 批量写入 if (dt.Rows.Count ==0)
{
Log.WriteLine(@"没有要写入的数据。");
return;
}
sw.Restart();
var sucess = false;
if (SqlHelper.BulkInsert(sqlconn, txt_TableName.Text.Trim(), dt, out var err))
{
sucess = true;
}
else
{
Log.Log.Error($"写入数据库失败==》{err}");
}
sw.Stop();
Log.WriteLine($"写入数据库是否成功=>{sucess},耗时{sw.ElapsedMilliseconds}毫秒");
#endregion
});
#endregion if (sqlconn.State == ConnectionState.Open)
{
sqlconn.Close();
}
button1.Enabled = true;
}));
}

  SQL批量写入函数

        /// <summary>
/// 批量插入
/// </summary>
/// <param name="conn">连接对象</param>
/// <param name="tableName">将泛型集合插入到本地数据库表的表名</param>
/// <param name="dataTable">要批量写入的Datatable</param>
/// <param name="err">错误时返回的信息</param>
public static bool BulkInsert(SqlConnection conn, string tableName, DataTable dataTable, out string err)
{
err = "";
if (dataTable == null || dataTable.Rows.Count == 0)
{
err = "要写入的数据为空";
return false;
}
var tran = conn.BeginTransaction();//开启事务
var bulkCopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.KeepNulls, tran);
try
{
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
bulkCopy.BatchSize = 1000;
bulkCopy.DestinationTableName = tableName;
bulkCopy.WriteToServer(dataTable);
tran.Commit();
return true;
}
catch (Exception e)
{
err = e.ToString();
tran.Rollback();
return false;
}
finally
{
bulkCopy.Close();
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
}
}

  

C#使用Parallel处理数据同步写入Datatable并使用BulkInsert批量导入数据库的更多相关文章

  1. 将Excle中的数据批量导入数据库

    namespace 将Excle中的数据批量导入数据库{    class Program    {        static void Main(string[] args)        { S ...

  2. ADO.NET 对数据操作 以及如何通过C# 事务批量导入数据

    ADO.NET 对数据操作 以及如何通过C# 事务批量导入数据   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ...

  3. Java实现Excel数据批量导入数据库

    Java实现Excel数据批量导入数据库 概述: 这个小工具类是工作中的一个小插曲哦,因为提数的时候需要跨数据库导数... 有的是需要从oracle导入mysql ,有的是从mysql导入oracle ...

  4. 将execl里的数据批量导入数据库

    本文将采用NPOI插件来读取execl文件里的数据,将数据加载到内存中的DataTable中 /// <summary> /// 将Excel转换为DataTable /// </s ...

  5. 关于Excel数据批量导入数据库的案例

    写这个案例主要是感觉这个功能挺实用,很多地方会用得到的,废话就不多说了,直接上对应的源码. 这个案例我运用的是Winform窗体程序实现数据的导入. 首先是数据库的登陆界面如下: 源码如下: usin ...

  6. 使用python,将excel数据批量导入数据库

    这是上一篇文章的优化版本,相较于一条一条的执行sql语句,本文中,将excel中所有的数据先写到list列表中 在通过函数 cursor.executemany(sql, list) 一次性写入到数据 ...

  7. C# NPOI的数据批量导入数据库

    public ActionResult Upload(HttpPostedFileBase Namefile)        {            //判断文件是否存在            if ...

  8. 将Excel中的数据批量导入数据库表

    private boolean import_to_database(String excel_path) throws BiffException, IOException, HsException ...

  9. MySQL中load data infile将文件中的数据批量导入数据库

    有时候我们需要将文件中的数据直接导入到数据库中,那么我们就可以使用load data infile,下面具体介绍使用方法. dao中的方法 @Autowired private JdbcTemplat ...

随机推荐

  1. python3.x Day1 菜单程序练习

    三级菜单: 1. 运行程序输出第一级菜单 2. 选择一级菜单某项,输出二级菜单,同理输出三级菜单 3. 菜单数据保存在文件中 4. 让用户选择是否要退出 5. 有返回上一级菜单的功能 类定义:menu ...

  2. 一:安装centos 7最小编程环境 xfce桌面

    1, u盘制作安装盘------------------------------------------------------安装时, table或者e进入编辑选项    如果不知道你的u盘的盘符 ...

  3. js中复制功能总结

    目前copy主流有四种方式:ZeroClipboard,Clipboard.js,execCommand,setData,clipboardData 概况: ZeroClipboard 就是常说的Fl ...

  4. String类的转换功能

    /* * String类的转换功能 * char[] toCharArray():把字符串转换为字符数组 * String toLowerCase():把字符串转换为小写字符串 * String to ...

  5. 【Codeforces 986B】Petr and Permutations

    [链接] 我是链接,点我呀:) [题意] 题意 [题解] n为奇数时3n和7n+1奇偶性不同 n为偶数时也是如此 然后交换任意一对数 逆序对的对数的奇偶性会发生改变一次 求出逆序对 对n讨论得出答案. ...

  6. Android第三方开源SwitchButton

    Android第三方开源SwitchButton Android SwitchButton是github上的一个第三方开源项目,其项目主页是:https://github.com/kyleduo/Sw ...

  7. HDU 1253 三维数组的图上找最短路

    题目大意: 从三维空间的(0,0,0)出发到(a-1,b-1,c-1),每移动一个都要时间加一,计算最短时间 根据六个方向,开个bfs,像spfa那样计算最短路径就行了,但是要1200多ms,也不知道 ...

  8. 小L 的二叉树(洛谷 U4727)

    题目背景 勤奋又善于思考的小L接触了信息学竞赛,开始的学习十分顺利.但是,小L对数据结构的掌握实在十分渣渣. 所以,小L当时卡在了二叉树. 题目描述 在计算机科学中,二叉树是每个结点最多有两个子结点的 ...

  9. SSH三种框架及表示层、业务层和持久层的理解(转)

    Struts(表示层)+Spring(业务层)+Hibernate(持久层) SSH:Struts(表示层)+Spring(业务层)+Hibernate(持久层) Struts:Struts是一个表示 ...

  10. 你的ExcelUtil简单、高效、易扩展吗

    你的ExcelUtil简单.高效.易扩展吗 Author: Dorae Date: 2018年10月23日12:30:15 转载请注明出处 一.背景 最近接到了和Excel导出相关的需求,但是: 项目 ...