首页 > 学院 > 开发设计 > 正文

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

2019-11-17 02:28:58
字体:
来源:转载
供稿:网友

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

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

阅读目录

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

线程的应用

这里先讲一下线程在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{    /// <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()));            }        }    }}
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 st
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表