這一篇將在之前的代碼生成器上講解多線程的應(yīng)用,多線程的概念和好處這里就不多說了,另外從本篇開始后面的實例代碼都將放到淘寶的SVN管理工具上維護,大家可以直接使用SVN工具進行下載。好了下面進入本篇內(nèi)容。
閱讀目錄
這里先講一下線程在Web程序中的一個應(yīng)用,之前的那一版代碼生成器沒有考慮表數(shù)量多的情形,這里先模擬一下在數(shù)據(jù)庫中創(chuàng)建300張表的情形,下面給出創(chuàng)建表的語句。

--模擬創(chuàng)建300張表,@IsDropTable=0 表示創(chuàng)建表 IsDropTable=1 表示刪除創(chuàng)建的模擬表DECLARE @IsDropTable AS BITDECLARE @total AS INT DECLARE @i AS INTSELECT @i=1,@total=300,@IsDropTable=0WHILE @i<=@totalBEGINDECLARE @strSQL AS VARCHAR(1000) --創(chuàng)建表 SELECT @strSQL=' CREATE TABLE myTest'+CONVERT(VARCHAR,@i)+' ( [UserGUID] [uniqueidentifier] NOT NULL ) EXEC sp_addextendedPRoperty N''MS_DescrView Code我們來看下執(zhí)行時間,差不多用了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">模版文件參數(shù)</param> public static void BatchGenerator(List<Hashtable> args) { if (!Directory.Exists(outPutPath)) { Directory.CreateDirectory(outPutPath); } //生成文件數(shù)量<Number10,則不開啟線程生成 if (args.Count < Number10) { DoWork(args); } else { //計算需要的線程數(shù) int threadCount = args.Count % Number10 == 0 ? args.Count / Number10 : args.Count / Number10 + 1; if (threadCount > MaxThreadCount) { threadCount = MaxThreadCount; } //每個線程需要生成的實體數(shù)量 int threadPqgeSize = (args.Count / threadCount) + 1; int total = 0; //為每個線程準備參數(shù) 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; } //創(chuàng)建線程 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]); } // 為當前線程指派生成任務(wù)。 DoWork(threadParams[0]); // 等待所有的編譯線程執(zhí)行線束。 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代碼思路,判斷要生成的實體數(shù)量和Number10的關(guān)系,然后計算所需的線程數(shù)。
關(guān)鍵的一點是thread.Join(),這段是主線程等待每個線程執(zhí)行完成。現(xiàn)在再來看下執(zhí)行時間,差不多用了13秒,節(jié)省了將近10S的時間。
回到頂部
winform程序中的多線程
下面來考慮這樣的一個場景,在生成了文件的時候馬上在列表中提示實體生成完成,即進度提示的功能。我們來看下winform中的兩種實現(xiàn)方式。
1.利用委托實現(xiàn)
先看一下普通線程實現(xiàn)方式,執(zhí)行的時候會拋出如下異常。
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 窗體控件本質(zhì)上不是線程安全的。如果有兩個或多個線程操作某一控件的狀態(tài),則可能會迫使該控件進入一種不一致的狀態(tài)。還可能出現(xiàn)其他與線程相關(guān)的 bug,包括爭用情況和死鎖。確保以線程安全方式訪問控件非常重要。
C#中禁止跨線程直接訪問控件,InvokeRequired是為了解決這個問題而產(chǎn)生的,當一個控件的InvokeRequired屬性值為真時,說明有一個創(chuàng)建它以外的線程想訪問它。
于是改變了思路,新建線程用以執(zhí)行耗時的生成代碼操作,在每生成一個實體時,通知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
新聞熱點
疑難解答