数据字典生成工具之旅(9):多线程使用及介绍

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

阅读目录

  • 线程的应用
  • winform程序中的多线程
  • 本章总结
  • 工具源代码下载
  • 学习使用

回到顶部

线程的应用

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

--模拟创建300张表,@IsDropTable=0 表示创建表 IsDropTable=1 表示删除创建的模拟表
DECLARE @IsDropTable AS BIT
DECLARE @total AS INT
DECLARE @i AS INT
SELECT @i=1,@total=300,@IsDropTable=0
WHILE @i<=@total
BEGIN
DECLARE @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+1
END

我们来看下执行时间,差不多用了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
{
    /// <summary>
    /// 代码生成类
    /// </summary>
    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;

        /// <summary>
        /// 批量生成代码
        /// </summary>
        /// <param name="args">模版文件参数</param>
        public static void BatchGenerator(List<Hashtable> args)
        {
            if (!Directory.Exists(outPutPath))
            {
                Directory.CreateDirectory(outPutPath);
            }
            //生成文件数量<Number10,则不开启线程生成
            if (args.Count < Number10)
            {
                DoWork(args);
            }
            else
            {
                //计算需要的线程数
                int threadCount = args.Count % Number10 == 0 ? args.Count / Number10 : args.Count / Number10 + 1;
                if (threadCount > MaxThreadCount)
                {
                    threadCount = MaxThreadCount;
                }

                //每个线程需要生成的实体数量
                int threadPqgeSize = (args.Count / threadCount) + 1;
                int total = 0;
                //为每个线程准备参数
                List<List<Hashtable>> threadParams = new List<List<Hashtable>>();
                for (int i = 0; i < threadCount; i++)
                {
                    threadParams.Add(args.Skip(total).Take(threadPqgeSize).ToList());
                    total += threadParams[i].Count;
                }
                //创建线程
                List<Thread> threads = new List<Thread>();
                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<Hashtable> list = (List<Hashtable>)listArgs;
            foreach (Hashtable ht in list)
            {
                FileGen.GetFile(tmpPath, ht, string.Format("{0}\\{1}.cs", outPutPath, ((DataTable)ht["T"]).Rows[0]["table_name"].ToString()));
            }
        }

    }
}

代码思路,判断要生成的实体数量和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);
            }

先看一下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();

        /// <summary>
        /// 消息发送请求委托
        /// </summary>
        /// <param name="Msg">消息</param>
        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);
        }

        /// <summary>
        /// 这里进行耗时的生成代码操作
        /// </summary>
        private void GeneratorFile()
        {
            //循环生成实体,并且在列表上显示进度
            DataTable dt = GetAllTableInfo();
            DataView dv = dt.DefaultView;
            var query = (from p in dt.AsEnumerable()
                         group p by new { TableId = p.Field<int>("tableid"), TableName = p.Field<string>("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();
                }
            }

        }

        /// <summary>
        /// 列表显示最新消息
        /// </summary>
        /// <param name="dt"></param>
        private void BindMessage(object dt)
        {
            dataGridView.DataSource = dt;
        }

        /// <summary>
        /// 获取所有表信息
        /// </summary>
        /// <returns></returns>
        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();
        }
    }
}

    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();

        /// <summary>
        /// 消息发送请求委托
        /// </summary>
        /// <param name="Msg">消息</param>
        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<int>("tableid"), TableName = p.Field<string>("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);
            }
        }

        /// <summary>
        /// 进度改变执行
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            dataGridView.DataSource = e.UserState;
        }

        /// <summary>
        /// 获取所有表信息
        /// </summary>
        /// <returns></returns>
        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();
        }
    }
}

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

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

回到顶部

本章总结

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

本章代码示例代码下载地址:http://code.taobao.org/svn/DataDic_QuickCode ,请使用SVN进行下载!

回到顶部

工具源代码下载

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

数据字典生成工具V2.0安装程序 最新安装程序
数据字典生成工具源代码 最新源代码

回到顶部

学习使用

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

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

时间: 2024-10-28 22:02:34

数据字典生成工具之旅(9):多线程使用及介绍的相关文章

数据字典生成工具之旅(6):NVelocity语法介绍及实例

本章开始将会为大家讲解NVelocity的用法,并带领大家实现一个简单的代码生成器. NVelocity是一个基于.NET的模板引擎(template engine).它允许任何人仅仅简单的使用模板语言(template language)来引用由.NET代码定义的对象.从而使得界面设计人员与.NET程序开发人员基本分离. 阅读目录 NVelocity的常用功能简介 基本语法 实例介绍 工具源代码下载 学习使用 回到顶部 NVelocity的常用功能简介 1. 在页面中定义变量,并进行简单的运算

数据字典生成工具之旅(5):DocX组件读取与写入Word

由于上周工作比较繁忙,所以这篇文章等了这么久才写(预告一下,下一个章节正式进入NVelocity篇,到时会讲解怎么使用NVelocity做一款简易的代码生成器,敬请期待!),好了正式进入本篇内容. 这篇会介绍DocX读写WORD,DocX组件功能强大,可以很容易的读写WORD,相对于NPOI强大很多,性能也好很多,做这个工具之所以会选择这个组件,主要是看重该组件的表格合并功能. 阅读目录 使用模版生成简历 读写表格数据 合并单元格 工具源代码下载 学习使用 回到顶部 使用模版生成简历 下面将以一

数据字典生成工具之旅(4):NPOI操作EXECL

这篇会介绍NPOI读写EXECL,读写EXECL的组件很多,可以使用微软自己的COM组件EXECL.exe读写,不过这种方式限制很大. 1:客户环境必须装Office(虽然现在机子上不装Office的几乎没有吧) 2:EXECL读写完毕后EXECL还有进程还留在后台  ,内存回收不了 基于以上的原因,就采用了开源组件NPOI进行操作了. 阅读目录 NPOI简介 简单示例 NPOI在本工具的使用及总结 工具源代码下载 学习使用 回到顶部 NPOI简介 1.Excel表格叫做工作表:WorkBook

数据字典生成工具之旅(2):数据字典生成工具及文档工具作用介绍

上一篇介绍完了整个架构和功能,这一篇将更加详细介绍功能和操作,将会以实际例子帮助理解!(预告:下一篇正式进入实现原理讲解) 阅读目录 开始使用工具 工具全景图 工具源代码下载 学习使用 回到顶部 开始使用工具 日常工作你是通过什么工具来创建表的呢? 1.通过SqlServer自带的新建表的功能实现,或者直接写Create Table(.....)语句.能更加便捷的方式创建吗?这样创建的表便于后续维护和学习吗? 2.通过PowerDesign创建表,很直观,然后导出表的创建语句.模拟一下做学生信息

数据字典生成工具之旅(8):SQL查询表的约束默认值等信息

上一篇代码生成工具里面已经用到了读取表结构的SQL,这篇将更加详细的介绍SQL SERVER常用的几张系统表和视图! 阅读目录 系统表视图介绍 实际应用 本章总结 工具源代码下载 学习使用 回到顶部 系统表视图介绍 1.sys.tables(用户表) SELECT name,object_id FROM sys.tables 上面SQL是用来查询数据库里面所有用户创建的表,name为表名,object_id为表的对象id.其中object_id的值也可以用系统函数OBJECT_ID()来取 SE

数据字典生成工具(生成Excel, Word,PDF,html)

转自:http://www.cnblogs.com/yanweidie/p/3838765.html 数据字典生成工具之旅系列文章导航 数据字典生成工具之旅系列文章导航 宣传语 数据字典生成工具.数据字典文档生成工具.NPOI入门.NPOI下载.NPOI中文教程.NPOI实例.DocX组件操作Word.PowerDesign读取.WORD读取和操作.NVelocity模版文件生成.数据字典生成工具之旅 导游 数据字典生成工具会根据模版文件生成创建表的SQL语句,包含SQL自动提示功能,操作更加快

数据字典生成工具

之前找的数据库字典生成工具基本上都依赖于 Office Com 组件,在不安装 Office的情况下无法使用.怒,于是自己用C# 写了一个. 特征如下: 一.支持的数据库 MS SQL Server 2005+.My Sql.Oracle 二.支持的文档类型 HTML.CHM. WORD 三.无需安装办公软件即可生成 WORD 格式的文件 四.基于 .net framework 3.5 框架,电脑上需要安装 .net framework 3.5. PS:欢迎反馈BUG ,反馈方式 戳 About

SQLite3源程序分析之Lemon分析器生成工具

1.概述 Lemon是一个LALR(1)文法分析器生成工具.虽然它是SQLite作者针对SQLite写的一个分析器生成工具,但是它与bison和yacc类似,是一个可以独立于SQLite使用的开源的分析器生成工具.而且它使用与yacc(bison)不同的语法规则,可以减少编程时出现错误的机会.Lemon比yacc和bison更精致.更快,而且是可重入的,也是线程安全的——这对于支持多线程的程序是非常重要的. Lemon的主要功能就是根据上下文无关文法(CFG),生成支持该文法的分析器.程序的输入

SQL Server2005+、MySQL、Oracle 数据库字典生成工具

之前找的数据库字典生成工具基本上都依赖于 Office Com 组件,在不安装 Office的情况下无法使用.怒,于是自己用C# 写了一个. 特征如下:    一.支持的数据库 MS SQL Server 2005+.My Sql.Oracle    二.支持的文档类型 Html.CHM.Docx    三.无需安装Office即可生成 Docx 格式的Word文件    四.基于 .net framework 3.5 框架,电脑上需要安装 .net framework 3.5.