多线程与委托
多线程在日常开发当中会经常遇到,比如在处理大数据量或并行处理一些数据和操作时都可能用到多线程。
委托也是一个比较好的技术点,如果运用适当,它能很大程度上减少我们的代码重复性,增加代码的可维护性。
下面就以我在公司中的运用实例来简要的说明一下这两种技术的使用。
首页这个功能的大体需求是通过应用程序读取文件中的数据,再与数据库进行一条条的操作,由于我们的文件数据量很大,十几兆或几十兆的都用,而这时如果我们只用单线程,通常跑完一个文件需要很久很久。所以为了让程序跑的更快,就想到了多线程处理。
下面我们主要说明对多线程的使用,如下代码所示:
public static void ExportDataByTXT(string fileTempFolder) { List<string> FileList = CommFunction.GetTempFolderFiles(fileTempFolder); if (FileList.Count == 0) { WriteLog("没有需要处理的文件!"); return; } foreach (string FileName in FileList) { //WriteLog("处理文件:" + FileName); if (FileName.Contains("cn_host_item.txt")) //基本信息 { WriteLog("开始处理文件:cn_host_item"); HostItemProcess(FileName); CommFunction.CopyFileToHistoryFolder(Folder, HistoryFolder + "\\" + DateTime.Now.ToString("yyyyMMdd"), "cn_host_item.txt"); WriteLog(string.Format("文件{0}处理完成", "cn_host_item.txt")); } } }
其中该方法是读取文件的CommFunction.GetTempFolderFiles方法,这里我们就以众多的文件中的其中一个来讲解我们的多线程,以文件cn_host_item.txt来说,HostItemProcess方法才是我们处理某一文件的重要方法之一。
public static void HostItemProcess(string FileName) { #region cn_host_item TCNSmartPrice smartPrice = new TCNSmartPrice(); List<FileDataEntity> ListFileData = CommFunction.GetDataListFromTxtHostItem(FileName, LineSplitTag , DataSplitTag, ref message); ListFileData.Reverse(); smartPrice.TotalCount = ListFileData.Count; int threadCount = Convert.ToInt32(ThreadNumber);//线程数。 if (ListFileData != null && ListFileData.Count > threadCount) { #region 分割List List<List<FileDataEntity>> splitList = new List<List<FileDataEntity>>(threadCount); int listCount = ListFileData.Count;//总的数量。 int size = listCount / threadCount; int otherData = listCount % threadCount; for (int i = 0; i < threadCount; i++) { int realCount; if (i != threadCount - 1) { realCount = size; } else { realCount = listCount - i * size; } splitList.Add(ListFileData.GetRange(i * size, realCount)); } message.AppendLine(string.Format("文件cn_host_item.txt导入情况: ")); message.AppendLine(string.Format("开始时间:{0}", DateTime.Now.ToString())); CNHostItemDelegate cnhostitem = CNHostItemOperationMethod; //多线程处理 CNHostItem cnHostItem = new CNHostItem(splitList,cnhostitem, ref smartPrice); while (true) { if (smartPrice.RunCount == smartPrice.TotalCount || smartPrice.SaveSuccessCount == smartPrice.TotalCount || (smartPrice.SaveSuccessCount + smartPrice.FailCount) == smartPrice.TotalCount) { break; } } Thread.Sleep(10000); message.AppendLine(string.Format("共计{0}条数据,更新成功{1}条数据,更新失败{2}", smartPrice.TotalCount, smartPrice.SaveSuccessCount, smartPrice.FailCount)); message.AppendLine(string.Format("结束时间:{0}", DateTime.Now.ToString())); if (smartPrice != null && smartPrice.SaveMessage != null && smartPrice.SaveMessage.Count > 0) { message.AppendLine("数据失败原因为:"); foreach (string er in smartPrice.SaveMessage) { message.AppendLine(er); } } #endregion } #endregion }
这个方法有几个重要的类,如下:
TCNSmartPrice 该类信息处理数据时统计,其中同级下还有委托的定义
namespace ReadtxtHostItemDataToDB.Threads { //定义一个委托 public delegate void CNHostItemDelegate(List<FileDataEntity> list, string threadName, ref TCNSmartPrice MarkInfo); public class TCNSmartPrice { public List<FileDataEntity> SaveFileDataEntity { get; set; } public List<List<FileDataEntity>> SaveListFileDataEntity { get; set; } public List<FileDataEntity> ZeaProductList { get; set; } /// <summary> /// 记录每次执行的信息 /// </summary> public List<string> SaveMessage { get; set; } /// <summary> /// 执行成功的数据量 /// </summary> public int SaveSuccessCount { get; set; } /// <summary> /// 需要执行的数据量 /// </summary> public List<int> SaveCount { get; set; } /// <summary> /// 总数据量 /// </summary> public double TotalCount { get; set; } /// <summary> /// 已执行数据量 /// </summary> public double RunCount { get; set; } /// <summary> /// 执行失败数据量 /// </summary> public double FailCount { get; set; } } }
CNHostItem类,此类是我们多线程运用的主要操作,其中包含多线程的创建,多线程和委托的使用,代码如下:
namespace ReadtxtHostItemDataToDB.Threads { public class CNHostItem { public CNHostItemDelegate _CNHostItemDelegate; object syncObj = new object(); private int ThreadNum = 0; private List<List<FileDataEntity>> SplitList = null; TCNSmartPrice MarkInfo = new TCNSmartPrice(); /// <summary> /// 构造函数,传递方法名 /// </summary> /// <param name="splitList"></param> /// <param name="smartprice"></param> public CNHostItem(List<List<FileDataEntity>> splitList, CNHostItemDelegate cnHostItemDelegate, ref TCNSmartPrice smartprice) { MarkInfo.SaveListFileDataEntity = splitList; MarkInfo.TotalCount = smartprice.TotalCount; MarkInfo.SaveMessage = new List<string>(); MarkInfo.SaveCount = new List<int>(); ThreadNum = MarkInfo.SaveListFileDataEntity.Count; _CNHostItemDelegate = cnHostItemDelegate; for (int i = 0; i < ThreadNum; i++) { Thread pingTask = new Thread(new ParameterizedThreadStart(Run)); pingTask.Name = "线程" + i; //线程名称 pingTask.Start(i); } smartprice = this.MarkInfo; } public void Run(object o) { int number = (int)o; if (number < ThreadNum) { GetShow(MarkInfo.SaveListFileDataEntity[number]); } } public void GetShow(List<FileDataEntity> list) { if (list != null && list.Count > 0) { //使用委托执行方法 _CNHostItemDelegate(list, Thread.CurrentThread.Name, ref MarkInfo); } } } }
以上就是多线程和委托的运用。下面讲解一下代码运行流程。
首页程序会进会HostItemProcess方法,进行读取文件的每条数据(CommFunction.GetDataListFromTxtHostItem是读取文件数据的),然后对这些数据进行分隔。
其次“CNHostItemDelegate cnhostitem = CNHostItemOperationMethod;”这句就是委托的使用,在这里我们将线程需要处理的方法以季托的形式传递过去,这个我们可以在不更改CNHostItem这个类的情况下,重复的使用各个文件的多线程操作。在这个类中我们可以看到此类的构造函数中
public CNHostItem(List<List<FileDataEntity>> splitList, CNHostItemDelegate cnHostItemDelegate, ref TCNSmartPrice smartprice)
有传递一个季托进来。(CNHostItemDelegate cnHostItemDelegate),这个就是我们优化代码复用性的操作,在GetShow方法中,我们就不需要各个文件调用不同的方法了。
最后就是我们所用到的多线程了,在CNHostItem类中我们动态的创建多个线程去执行我们所要操作的方法(此例的CNHostItemOperationMethod方法),在这里我们需要注意以下问题,在多线程操作时,如果下面还有代码要操作,需要注意两点:
一, 如果下面代码与多线程没有关系,则在多线程还未执行完时就会执行下面代码,不过此问题不大。
如果下面的代码与多线程有关系,则必须要等多线程执行完毕才能执行下面代码,这就如我们文档中所写加上了如下代码:
while (true) { if (smartPrice.RunCount == smartPrice.TotalCount || smartPrice.SaveSuccessCount == smartPrice.TotalCount || (smartPrice.SaveSuccessCount + smartPrice.FailCount) == smartPrice.TotalCount) { break; } } Thread.Sleep(10000);
此段代码就是为了等线程执行完我们要执行的操作后再去执行下面代码,如果未加此功能,将会导致,线程还未执行完操作时,线程下面代码已经执行完了,所以最终显示的信息就会不正确了。
浙公网安备 33010602011771号