博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数据字典生成工具之旅(9):多线程使用及介绍
阅读量:7251 次
发布时间:2019-06-29

本文共 14821 字,大约阅读时间需要 49 分钟。

      这一篇将在之前的代码生成器上讲解多线程的应用,多线程的概念和好处这里就不多说了,另外从本篇开始后面的实例代码都将放到上维护,大家可以直接使用SVN工具进行下载。好了下面进入本篇内容。

阅读目录

线程的应用

     这里先讲一下线程在Web程序中的一个应用,之前的那一版代码生成器没有考虑表数量多的情形,这里先模拟一下在数据库中创建300张表的情形,下面给出创建表的语句 。

--模拟创建300张表,@IsDropTable=0 表示创建表 IsDropTable=1 表示删除创建的模拟表DECLARE @IsDropTable AS BITDECLARE @total AS INT DECLARE @i AS INTSELECT @i=1,@total=300,@IsDropTable=0WHILE @i<=@totalBEGINDECLARE @strSQL AS VARCHAR(1000)    --创建表    SELECT @strSQL='        CREATE TABLE myTest'+CONVERT(VARCHAR,@i)+'        (            [UserGUID] [uniqueidentifier] NOT NULL        )        EXEC sp_addextendedproperty N''MS_Description'', N''用户表'', ''SCHEMA'', N''dbo'', ''TABLE'', N''myTest'+CONVERT(VARCHAR,@i)+''', NULL, NULL        EXEC sp_addextendedproperty N''MS_Description'', N''用户GUID'', ''SCHEMA'', N''dbo'', ''TABLE'', N''myTest'+CONVERT(VARCHAR,@i)+''', ''COLUMN'', N''UserGUID''    '        IF @IsDropTable=1    BEGIN        --删除表        SELECT @strSQL='DROP TABLE myTest'+CONVERT(VARCHAR,@i)    END        EXEC(@strSQL)    SELECT @i=@i+1END
View Code

    我们来看下执行时间,差不多用了22秒,时间还是挺长的。可以将代码改造一下,使用多线程来生成代码。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Web;using System.Configuration;using System.Collections;using System.IO;using System.Data;using System.Threading;namespace Mysoft.Code.Services{    ///     /// 代码生成类    ///     public class CodeGenerator    {        //模版文件路径        private static string tmpPath = HttpContext.Current.Server.MapPath("/实体模版/Entity.vm");        //模版输出路径        private static string outPutPath = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["outputPath"]);        private static readonly int Number10 = 400;        private static readonly int MaxThreadCount = 4;        ///         /// 批量生成代码        ///         /// 模版文件参数        public static void BatchGenerator(List
args) { if (!Directory.Exists(outPutPath)) { Directory.CreateDirectory(outPutPath); } //生成文件数量
MaxThreadCount) { threadCount = MaxThreadCount; } //每个线程需要生成的实体数量 int threadPqgeSize = (args.Count / threadCount) + 1; int total = 0; //为每个线程准备参数 List
> threadParams = new List
>(); for (int i = 0; i < threadCount; i++) { threadParams.Add(args.Skip(total).Take(threadPqgeSize).ToList()); total += threadParams[i].Count; } //创建线程 List
threads = new List
(); for (int i = 1; i < threadCount; i++) { Thread thread = new Thread(DoWork); thread.IsBackground = true; thread.Name = "CodeGenerator #" + i.ToString(); threads.Add(thread); thread.Start(threadParams[i]); } // 为当前线程指派生成任务。 DoWork(threadParams[0]); // 等待所有的编译线程执行线束。 foreach (Thread thread in threads) { thread.Join(); } } } private static void DoWork(Object listArgs) { List
list = (List
)listArgs; foreach (Hashtable ht in list) { FileGen.GetFile(tmpPath, ht, string.Format("{0}\\{1}.cs", outPutPath, ((DataTable)ht["T"]).Rows[0]["table_name"].ToString())); } } }}
View Code

    代码思路,判断要生成的实体数量和Number10的关系,然后计算所需的线程数。

   关键的一点是 thread.Join(),这段是主线程等待每个线程执行完成。现在再来看下执行时间,差不多用了13秒,节省了将近10S的时间。

 

 

winform程序中的多线程

    下面来考虑这样的一个场景,在生成了文件的时候马上在列表中提示实体生成完成,即进度提示的功能。我们来看下winform中的两种实现方式。

    1.利用委托实现

     先看一下普通线程实现方式,执行的时候会抛出如下异常。

foreach (var key in query)            {                dv.RowFilter = "tableid=" + key.tableid;                DataTable dtTable = dv.ToTable();                Hashtable ht = new Hashtable();                ht["T"] = dtTable;                string tableName = dtTable.Rows[0]["table_name"].ToString();                FileGen.GetFile(tmpPath, ht, string.Format("{0}\\{1}.cs", outPutPath, tableName));                Thread thread = new Thread(BindMessage);                thread.IsBackground = true;                thread.Name = "BindMessage:" + key.tableid;                thread.Start(tableName);            }
View Code

     先看一下msdn的介绍:

       访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。

  C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它。

      于是改变了思路,新建线程用以执行耗时的生成代码操作,在每生成一个实体时,通知UI线程更新dataGridView,达到实时更新的效果,这样主线程也不会阻塞了。

  

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using Mysoft.Map.Extensions.DAL;using System.Collections;using Mysoft.Code.Services;using System.IO;using System.Threading;namespace ThreadWin{    public partial class MainForm : Form    {        //模版文件路径        private static string tmpPath = AppDomain.CurrentDomain.BaseDirectory + @"实体模版\Entity.vm";        //模版输出路径        private static string outPutPath = AppDomain.CurrentDomain.BaseDirectory + @"模版输出路径";        private DataTable dtInfo = new DataTable();        ///         /// 消息发送请求委托        ///         /// 消息        delegate void SetMessageCallBack(object Msg);        public MainForm()        {            InitializeComponent();            dtInfo.Columns.Add("TableName");            dtInfo.Columns.Add("Info");            dtInfo.Columns.Add("Time");            Control.CheckForIllegalCrossThreadCalls = false;        }        private void btn_OK_Click(object sender, EventArgs e)        {            dtInfo.Clear();                        if (!Directory.Exists(outPutPath))            {                Directory.CreateDirectory(outPutPath);            }            //1.耗时的操作放在新建线程里面执行            Thread thread = new Thread(GeneratorFile);            thread.IsBackground = true;            thread.Start();            //2.使用系统的线程池进行线程操作            //ThreadPool.QueueUserWorkItem(GeneratorFile);        }        ///         /// 这里进行耗时的生成代码操作        ///         private void GeneratorFile()        {            //循环生成实体,并且在列表上显示进度            DataTable dt = GetAllTableInfo();            DataView dv = dt.DefaultView;            var query = (from p in dt.AsEnumerable()                         group p by new { TableId = p.Field
("tableid"), TableName = p.Field
("table_name") } into q select new { TableId = q.Key.TableId, TableName = q.Key.TableName } ); foreach (var key in query) { dv.RowFilter = "tableid=" + key.TableId; DataTable dtTable = dv.ToTable(); Hashtable ht = new Hashtable(); ht["T"] = dtTable; string tableName = dtTable.Rows[0]["table_name"].ToString(); FileGen.GetFile(tmpPath, ht, string.Format("{0}\\{1}.cs", outPutPath, key.TableName)); //消息提示 DataRow dr = dtInfo.NewRow(); dr["TableName"] = tableName; dr["Info"] = "生成成功"; dr["Time"] = DateTime.Now.ToString(); dtInfo.Rows.Add(dr); DataView dvOrder = dtInfo.DefaultView; dvOrder.Sort = "Time DESC"; DataTable dtinfo = dvOrder.ToTable(); if (this.dataGridView.InvokeRequired) { SetMessageCallBack stms = new SetMessageCallBack(BindMessage); if (this != null) { this.Invoke(stms, new object[] { dtinfo }); } } else { dataGridView.DataSource = dvOrder.ToTable(); } } } ///
/// 列表显示最新消息 /// ///
private void BindMessage(object dt) { dataGridView.DataSource = dt; } ///
/// 获取所有表信息 /// ///
public DataTable GetAllTableInfo() { string strSQL = @" SELECT T.name AS table_name , T.object_id AS tableid, ISNULL(CONVERT(VARCHAR(MAX), E.value), '') AS table_name_c , C.name AS field_name , ISNULL(CONVERT(VARCHAR(MAX), D.value), '') AS field_name_c , ROW_NUMBER() OVER(PARTITION BY T.name ORDER BY C.colid) AS field_sequence , TYPE_NAME(C.xtype) AS date_type , (CASE WHEN EXISTS ( SELECT 1 FROM sysobjects WHERE xtype = 'PK' AND name IN ( SELECT name FROM sysindexes WHERE id = C.id AND indid IN ( SELECT indid FROM sysindexkeys WHERE id = C.id AND colid = C.colid ) ) ) THEN 1 ELSE 0 END) AS pk , ISNULL(C.isnullable, 1) AS isnullable , ISNULL(COLUMNPROPERTY(c.id, c.name, 'IsIdentity'), 0) AS isidentity FROM sys.tables AS T LEFT JOIN syscolumns AS C ON c.id = T.object_id LEFT JOIN sys.extended_properties AS D ON D.major_id = T.object_id AND D.minor_id = C.colid AND D.major_id = C.id LEFT JOIN sys.extended_properties AS E ON E.major_id = T.object_id AND E.minor_id = 0 WHERE T.object_id IN (SELECT object_id FROM sys.Tables)"; return CPQuery.From(strSQL).FillDataTable(); } }}
View Code

    2.BackgroundWorker

     除了自己使用Thread或者ThreadPool来实现跨线程更新UI还可以使用BackgroundWorker组件来实现该效果。

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using Mysoft.Map.Extensions.DAL;using System.Collections;using Mysoft.Code.Services;using System.IO;using System.Threading;namespace ThreadWin{    public partial class BackgroundWorkerForm : Form    {        //模版文件路径        private static string tmpPath = AppDomain.CurrentDomain.BaseDirectory + @"实体模版\Entity.vm";        //模版输出路径        private static string outPutPath = AppDomain.CurrentDomain.BaseDirectory + @"模版输出路径";        private DataTable dtInfo = new DataTable();        ///         /// 消息发送请求委托        ///         /// 消息        delegate void SetMessageCallBack(object Msg);        public BackgroundWorkerForm()        {            InitializeComponent();            dtInfo.Columns.Add("TableName");            dtInfo.Columns.Add("Info");            dtInfo.Columns.Add("Time");            Control.CheckForIllegalCrossThreadCalls = false;        }        private void btn_OK_Click(object sender, EventArgs e)        {            dtInfo.Clear();                        if (!Directory.Exists(outPutPath))            {                Directory.CreateDirectory(outPutPath);            }            //判断线程是否Busy            if (mBackgroundWorker.IsBusy)            {                MessageBox.Show("当前进程正在生成代码,请等待本次操作完成!");                return;            }            mBackgroundWorker.RunWorkerAsync();        }        private void mBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)        {            BackgroundWorker bw = (BackgroundWorker)sender;            //循环生成实体,并且在列表上显示进度            DataTable dt = GetAllTableInfo();            DataView dv = dt.DefaultView;            var query = (from p in dt.AsEnumerable()                         group p by new { TableId = p.Field
("tableid"), TableName = p.Field
("table_name") } into q select new { TableId = q.Key.TableId, TableName = q.Key.TableName } ); foreach (var key in query) { dv.RowFilter = "tableid=" + key.TableId; DataTable dtTable = dv.ToTable(); Hashtable ht = new Hashtable(); ht["T"] = dtTable; string tableName = dtTable.Rows[0]["table_name"].ToString(); FileGen.GetFile(tmpPath, ht, string.Format("{0}\\{1}.cs", outPutPath, key.TableName)); //消息提示 DataRow dr = dtInfo.NewRow(); dr["TableName"] = tableName; dr["Info"] = "生成成功"; dr["Time"] = DateTime.Now.ToString(); dtInfo.Rows.Add(dr); DataView dvOrder = dtInfo.DefaultView; dvOrder.Sort = "Time DESC"; DataTable dtinfo = dvOrder.ToTable(); //通知进度改变 bw.ReportProgress(0, dtinfo); } } ///
/// 进度改变执行 /// ///
///
private void mBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { dataGridView.DataSource = e.UserState; } ///
/// 获取所有表信息 /// ///
public DataTable GetAllTableInfo() { string strSQL = @" SELECT T.name AS table_name , T.object_id AS tableid, ISNULL(CONVERT(VARCHAR(MAX), E.value), '') AS table_name_c , C.name AS field_name , ISNULL(CONVERT(VARCHAR(MAX), D.value), '') AS field_name_c , ROW_NUMBER() OVER(PARTITION BY T.name ORDER BY C.colid) AS field_sequence , TYPE_NAME(C.xtype) AS date_type , (CASE WHEN EXISTS ( SELECT 1 FROM sysobjects WHERE xtype = 'PK' AND name IN ( SELECT name FROM sysindexes WHERE id = C.id AND indid IN ( SELECT indid FROM sysindexkeys WHERE id = C.id AND colid = C.colid ) ) ) THEN 1 ELSE 0 END) AS pk , ISNULL(C.isnullable, 1) AS isnullable , ISNULL(COLUMNPROPERTY(c.id, c.name, 'IsIdentity'), 0) AS isidentity FROM sys.tables AS T LEFT JOIN syscolumns AS C ON c.id = T.object_id LEFT JOIN sys.extended_properties AS D ON D.major_id = T.object_id AND D.minor_id = C.colid AND D.major_id = C.id LEFT JOIN sys.extended_properties AS E ON E.major_id = T.object_id AND E.minor_id = 0 WHERE T.object_id IN (SELECT object_id FROM sys.Tables)"; return CPQuery.From(strSQL).FillDataTable(); } }}
View Code

    1.操作步骤很简单,从组件里面拖一个BackgroundWorker组件设置WorkerReportsProgress(是否允许通知进度改变)为true

    2.添加DoWork(进行耗时操作) 和 ProgressChanged(进度改变执行) 方法

本章总结

     在写数据字典生成工具之前自己对线程的使用还是很模糊的,翻了很多资料和博客才学习到这些知识。如果您感觉本文不错,对您有所帮助,请您不吝点击下右边的推荐按钮,谢谢!

     本章代码示例代码下载地址: ,请使用SVN进行下载!

工具源代码下载

      目前总共有经过了七个版本的升级,现在提供最新版本的下载地址

最新安装程序
最新源代码
SVN最新源码共享地址

学习使用

      如果你使用了该工具,或者想学习该工具,欢迎加入这个小组,一起讨论数据字典生成工具、把该工具做的更强,更方便使用,一起加入

      更多数据字典生成工具资料请点击。

转载地址:http://ophbm.baihongyu.com/

你可能感兴趣的文章
服务器类型
查看>>
安装VIM8和vim-go插件
查看>>
安装SCCM2012 R2
查看>>
CentOS6.5 NFS服务器的安装与基本参数
查看>>
I/O多路转接之select
查看>>
让有些“-l”链接静态库,而另一些链接共享库?
查看>>
使用Webstorm操作git
查看>>
uboot移植之start_armboot()函数分析
查看>>
移动办公是不能阻挡的未来办公趋势
查看>>
docker简单介绍及安装
查看>>
DNS服务(1)基本概念详解
查看>>
Redhat7DNS搭建
查看>>
python之rabbitMQ
查看>>
sys和shutil
查看>>
Django模板——html转义
查看>>
数理统计总结篇第一章
查看>>
javascript开发模式
查看>>
Docker底层技术
查看>>
明全策:黄金波段上攻1345一线,1.24现货伦敦金分析策略
查看>>
【小松教你手游开发】【unity实用技能】unity ngui wp8上使用动态字体消失或碎片化的问...
查看>>