C# ASP.NET 线程入门教程
线程入门
创建线程
using system; using system.Threading; ThreadStart entry = new ThreadStart(CalcSum); Thread workThread = new Thread(entry); //or Thread workThread = new Thread(new ThreadStart(CalcSum)); //Method: static void CalcSum() { //do somethings. }
Thread 类几个重要方法
Thread 类用于创建线程,ThreadPool 类用于管理线程池。
Thread 类中几个重要的方法: 1. Start():启动线程; 2. Sleep(int):静态方法,暂停当前线程指定的毫秒数; 3. Abort():通常使用该方法来终止一个线程; 4. Suspend():该方法并不终止未完成的线程,它仅仅挂起线程,以后还可恢复; 5. Resume():恢复被 Suspend() 方法挂起的线程的执行。
Thread.ThreadState 属性
Thread.ThreadState 在各种情况下的取值如下: 1. Aborted:线程已停止 2. AbortRequested:线程的 Thread.Abort() 方法已被调用,但是线程还未停止 3. Background:线程在后台执行,与属性 Thread.IsBackground 有关(前台所有进程) 4. Running:线程正在正常运行 5. Stopped:线程已经被停止 6. StopRequested:线程正在被要求停止 7. Suspended:线程已经被挂起(此状态下,可以通过调用 Resume() 方法重新运行) 8. SuspendRequested:线程正在要求被挂起,但是未来得及响应 9. Unstarted:未调用 Thread.Start() 开始线程的运行 10. WaitSleepJoin:线程因为调用了 Wait(),Sleep() 或 Join() 等方法处于封锁状态
设定线程优先级
优先级由高到低分别是 Highest,AboveNormal,Normal,BelowNormal,Lowest。默认为 ThreadPriority.Normal。
//示例:设定优先级为最低 myThread.Priority=ThreadPriority.Lowest;
线程的同步和通讯——生产者和消费者
假设这样一种情况,两个线程同时维护一个队列,如果一个线程对队列中添加元素,而另外一个线程从队列中取用元素,那么我们称添加元素的线程为生产者,称取用元素的线程为消费者。
lock 关键字
lock 关键字解决多个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果的问题。 lock 关键字将一段代码定义为互斥段(critical section)。互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。
//定义如下: lock(expression) statement_block //expression 代表你希望跟踪的对象,通常是对象引用。一般地,保护一个类的实例,可以使用 this;保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以。 //statement_block 就是互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。
Monitor 类(System.Threading)
Monitor 提供了使线程共享资源的方案。Monitor 类可以锁定一个对象,一个线程只有得到这把锁才可以对该对象进行操作。
...... Queue oQueue=new Queue(); ...... Monitor.Enter(oQueue); ......//现在 oQueue 对象只能被当前线程操纵了 Monitor.Exit(oQueue);//释放锁 // 为了保证线程最终都能释放锁,你可以把 Monitor.Exit() 方法写在 try-catch-finally 结构中的 finally 代码块里。 // 当拥有对象锁的线程准备释放锁时,它使用 Monitor.Pulse() 方法通知等待队列中的第一个线程。
Monitor.Wait() 和 Monitor.Pulse()
1. Wait() 就是交出锁的使用权,使线程处于阻塞状态,直到再次获得锁的使用权。 2. 当前线程调用 Pulse() 向队列中的下一个线程发出锁的信号。接收到脉冲后,等待线程就被移动到就绪队列中。 在调用 Pulse 的线程释放锁后,就绪队列中的下一个线程(不一定是接收到脉冲的线程)将获得该锁。pulse() 并不会使当前线程释放锁。
实例:开辟一线程实现异步导出 Excel
环境介绍及实例简述
环境介绍: 开发语言》C#; 开发工具》Visual studio 2015; Asp.Net MVC Version》5.2.3; .Net Version》6.1.3; NIPO version》2.2.1;
实例简述: 由于导出的 Excel 文件比较大,非常耗时,为了不影响对界面的其他操作,需要采用异步的方式进行导出。 具体实现方法就是后台开辟一个线程将 Excel 导出到指定目录,然后提供下载。
实现思路及准备工作
思路: 通过线程实现异步导出操作; 通过 NIPO 组件将数据存到 Excel 文件中。
准备工作: 1.下载 NPOI 组件, http://npoi.codeplex.com/ 2.orcleHelper.dll
实例代码
UserController.cs
using Project.BLL; using Project.Class; using Project.Interface; using Project.ViewModel; using System; using System.Collections.Generic; using System.IO; using System.Web.Mvc; using System.Web.Script.Serialization; namespace Project.UI { /// <summary> /// 文件信息类 /// </summary> public class FileInfoClass { public int count { set; get; } public IList<string> d_fileList { set; get; } } /// <summary> /// 用户控制器 /// </summary> public class UserController : BaseController { //用户接口 private IUserBLL iuser = new UserBLL(); //返回信息 private ReturnInfo returninfo = new ReturnInfo(); /// <summary> /// 导出用户数据,返回文件列表 /// </summary> public ActionResult UserInfoExportExcel() { //搜索条件 Model 对象 SearchUserModel searchUserModel = new SearchUserModel(); //1.获取数据(具体如何获取数据,这里没有陈述) List<UserViewModel> list = iuser.GetUserInfo(searchUserModel, ref returninfo) as List<UserViewModel>; //2.调用方法,导出 Excel //生成文件名称(改文件名称) var fileName = string.Format("{0}用户信息表.xls", DateTime.Now.ToString("yyyyMMddHHssmm")); //判断目录是否存在(该目录名称) if (!Directory.Exists(Server.MapPath("~/Downloads/用户信息"))) { Directory.CreateDirectory(Server.MapPath("~/Downloads/用户信息")); } //将生成的文件保存到服务器临时文件夹中 string fullPath = Path.Combine(Server.MapPath("~/Downloads/用户信息"), fileName); //表头 Dictionary<string, string> tableHeader = new Dictionary<string, string> { { "user_id","用户编号" }, { "username","用户名" }, { "sex","性别" }, { "age","年龄" }, { "tel","联系电话"}, { "email","邮箱"}, { "user_type", "用户类型" }, { "nickname", "用户昵称" } }; //导出到 Excel。(Global.asax.cs) MvcApplication._VehicleQueueT.Enqueue(new Classes.DataExportPara { excelPath=fullPath, sheetName = "用户信息", tableHeard= tableHeader, list =list}); //获取路径 string path = Server.MapPath("~/Downloads/用户信息"); //获取所有 xls 文件路径 IList<string> fileList = GetAllFileName(path); FileInfoClass f_info = new FileInfoClass(); f_info.count = fileList.Count; f_info.d_fileList = fileList; //返回文件列表 return new ContentResult { Content = new JavaScriptSerializer { MaxJsonLength = Int32.MaxValue }.Serialize(f_info), ContentType = "application/json" }; } /// <summary> /// 仅获取文件列表 /// </summary> /// <returns></returns> public ActionResult GetFileLists() { if (!Directory.Exists(Server.MapPath("~/Downloads/用户信息"))) { Directory.CreateDirectory(Server.MapPath("~/Downloads/用户信息")); } //路径 string path = Server.MapPath("~/Downloads/用户信息"); // IList<string> fileList = GetAllFileName(path); FileInfoClass f_info = new FileInfoClass(); f_info.count = fileList.Count; f_info.d_fileList = fileList; return new ContentResult { Content = new JavaScriptSerializer { MaxJsonLength = Int32.MaxValue }.Serialize(f_info), ContentType = "application/json" }; } /// <summary> /// 删除文件 /// </summary> /// <param name="fileName"></param> /// <returns></returns> public ActionResult DeleteFile(string fileName) { string result = ""; //路径 string filePath = Server.MapPath("~/Downloads/用户信息/"+fileName); if (!Directory.Exists(Server.MapPath("~/Downloads/已删除文件目录"))) { Directory.CreateDirectory(Server.MapPath("~/Downloads/已删除文件目录")); } string deletedFilePath = Server.MapPath("~/Downloads/已删除文件目录/"+fileName); try { //System.IO.File.Delete(filePath); //移动文件到"已删除文件目录"中 FileInfo file = new FileInfo(filePath); file.MoveTo(deletedFilePath); result += "成功删除文件"; } catch (Exception) { result += "删除文件失败"; } //返回操作结果 return Json(result, JsonRequestBehavior.AllowGet); } /// <summary> /// 获取目录下的所有 xls 文件 /// </summary> /// <param name="path"></param> /// <returns></returns> private IList<string> GetAllFileName(string path) { /*List<FileInfo> filelist = new List<FileInfo>(); //if (System.IO.File.Exists(path+"\\*.xls")) var files = Directory.GetFiles(path, "*.xls"); foreach (var file in files) { filelist.Add(new FileInfo(file)); }*/ IList<string> list = new List<string>(); DirectoryInfo folder = new DirectoryInfo(path); foreach (FileInfo file in folder.GetFiles("*.xls")) { list.Add(file.Name); } return list; } } }
Global.asax.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using Project.Classes; namespace GpsProject.UI { public class MvcApplication : System.Web.HttpApplication { //数据导出队列 public static Queue<DataExportPara> _VehicleQueueT = new Queue<DataExportPara>(); protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); OutputVehicleExcel();//注册信息导出方法 } /// <summary> /// 导出信息 /// </summary> public static void OutputVehicleExcel() { DataExportPara exportPara = null; ThreadPool.QueueUserWorkItem(o => { while (true) { if (_VehicleQueueT != null && _VehicleQueueT.Count > 0) { exportPara = _VehicleQueueT.Dequeue(); if (exportPara != null) { //调用方法 DataExport.ExportExcel(exportPara.excelPath, exportPara.sheetName, exportPara.tableHeard, exportPara.list); } else { Thread.Sleep(6000); } } else { Thread.Sleep(6000); } } }); } } }
DataExportPara.cs
using System.Collections; using System.Collections.Generic; namespace Project.Classes { /// <summary> /// 数据导出 para /// </summary> public class DataExportPara { /// <summary> /// 导出路径 /// </summary> public string excelPath { get; set; } /// <summary> /// 数据列表 /// </summary> public IList list { get; set; } /// <summary> /// 工作表名称 /// </summary> public string sheetName { get; set; } /// <summary> /// 表头 /// </summary> public Dictionary<string, string> tableHeard {get;set;} } }
DataExport.cs
using GpsProject.Class; using System.Collections; using System.Collections.Generic; using System.IO; namespace Project.Classes { /// <summary> /// 数据导出 /// </summary> public class DataExport { /// <summary> /// 导出 Excel 到目录 /// </summary> /// <param name="path"></param> /// <param name="sheetName"></param> /// <param name="tableHeard"></param> /// <param name="list"></param> public static void ExportExcel(string path, string sheetName, Dictionary<string, string> tableHeard, IList list) { using (var exportData = NPOIExcelHelper.ExportToExcelStream(list, sheetName, tableHeard)) { //创建一个文件 FileStream file = new FileStream(path, FileMode.Create, FileAccess.Write); exportData.WriteTo(file); file.Close(); } } } }
NPOIExcelHelper.cs
using System; using System.Collections.Generic; using System.IO; using System.Linq; using NPOI.SS.UserModel; using System.Collections; namespace Project.Class { /// <summary> /// NPOI /// </summary> public class NPOIExcelHelper { /// <summary> /// 导出 Excel 到文件流 /// </summary> /// <param name="dt"></param> /// <param name="sheetName"></param> /// <param name="tableHeard"></param> /// <returns>文件流</returns> public static MemoryStream ExportToExcelStream(IList lists, string sheetName, Dictionary<string, string> tableHeard) { //创建一个工作簿 NPOI.HSSF.UserModel.HSSFWorkbook book = new NPOI.HSSF.UserModel.HSSFWorkbook(); NPOI.SS.UserModel.ISheet sheet = book.CreateSheet(sheetName); //创建 sheet //Excel 表头 NPOI.SS.UserModel.IRow row = sheet.CreateRow(0); //创建行 ICellStyle style = book.CreateCellStyle(); //创建单元格 style.Alignment = HorizontalAlignment.Center; //对齐方式 style.VerticalAlignment = VerticalAlignment.Center; //单元格居中对齐 #region 设置表头 List<string> headers = tableHeard.Keys.ToList(); for (int i = 0; i < headers.Count; i++) { ICell cell = row.CreateCell(i); cell.CellStyle = style; cell.SetCellValue(tableHeard[headers[i]]); } /*for (int i = 0; i < dt.Columns.Count; i++) { ICell cell = row.CreateCell(i); cell.CellStyle = style; cell.SetCellValue(dt.Columns[i].ColumnName); }*/ #endregion #region 填充数据 int rowIndex = 1;// 从第二行开始赋值(第一行已设置为单元头) if (lists != null && lists.Count > 0) { foreach (var list in lists) { IRow rowTemp = sheet.CreateRow(rowIndex); for (int i = 0; i < headers.Count; i++) { string cellValue = ""; // 单元格的值 object properotyValue = null; // 属性的值 System.Reflection.PropertyInfo properotyInfo = null; // 属性的信息 if (headers[i].IndexOf(".") > 0) { // 3.1.1 解析子类属性(这里只解析 1 层子类,多层子类未处理) string[] properotyArray = headers[i].Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries); string subClassName = properotyArray[0]; // '.'前面的为子类的名称 string subClassProperotyName = properotyArray[1]; // '.'后面的为子类的属性名称 System.Reflection.PropertyInfo subClassInfo = list.GetType().GetProperty(subClassName); // 获取子类的类型 if (subClassInfo != null) { // 3.1.2 获取子类的实例 var subClassEn = list.GetType().GetProperty(subClassName).GetValue(list, null); // 3.1.3 根据属性名称获取子类里的属性类型 properotyInfo = subClassInfo.PropertyType.GetProperty(subClassProperotyName); if (properotyInfo != null) { properotyValue = properotyInfo.GetValue(subClassEn, null); // 获取子类属性的值 } } } else { // 3.2 若不是子类的属性,直接根据属性名称获取对象对应的属性 properotyInfo = list.GetType().GetProperty(headers[i]); if (properotyInfo != null) { properotyValue = properotyInfo.GetValue(list, null); } } // 3.3 属性值经过转换赋值给单元格值 if (properotyValue != null) { cellValue = properotyValue.ToString(); // 3.3.1 对时间初始值赋值为空 if (cellValue.Trim() == "0001/1/1 0:00:00" || cellValue.Trim() == "0001/1/1 23:59:59") { cellValue = ""; } } // 3.4 填充到 Excel 的单元格里 ICell icellcontent = rowTemp.CreateCell(i); //icellcontent.CellStyle = Getcellstyle(workbook, cellStylecontent, fontcontent, stylexls.默认); icellcontent.SetCellValue(cellValue); } rowIndex++; //达到 65535 行,跳出循环 if (rowIndex== 65535) { break; } } } else { //导出空数据 sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(2, 2, 0, headers.Count - 1)); IRow row2 = sheet.CreateRow(1); ICell icellkong = row2.CreateCell(0); // icellkong.CellStyle = Getcellstyle(workbook, stylexls.默认); string str = "没有满足条件的数据可导出"; icellkong.SetCellValue(str); } /* for (int i = 1; i <= dt.Rows.Count; i++)//遍历 DataTable 行 { DataRow dataRow = dt.Rows[i - 1]; row = sheet.CreateRow(i);//在工作表中添加一行 for (int j = 0; j < dt.Columns.Count; j++)//遍历 DataTable 列 { ICell cell = row.CreateCell(j);//在行中添加一列 cell.SetCellValue(dataRow[j].ToString());//设置列的内容 } }*/ #endregion MemoryStream ms = new MemoryStream(); book.Write(ms); return ms; //返回文件流 } } }
小结
在阅读别人的文章时,认真。
实例:利用线程监听模拟车辆进出智能化停车场
实例简述
实现效果
主要代码
Main.cs
private void Main_Load(object sender, EventArgs e) { MessagePrint.UpdateEventInfo += new MessagePrint.ShowEventInfoHandler(UpdateRuntimeInfo); //设置开始、结束时间为 00:00:00 dateTimePicker_InStart.Text = ConfigInfoHelper.InStartRunTime; dateTimePicker_InEnd.Text = ConfigInfoHelper.InEndRunTime; dateTimePicker_OutStart.Text = ConfigInfoHelper.OutStartRunTime; dateTimePicker_OutEnd.Text = ConfigInfoHelper.OutEndRunTime; //获取默认时间 BTModel.InStartRunTime = ConfigInfoHelper.InStartRunTime; BTModel.InEndRunTime = ConfigInfoHelper.InEndRunTime; BTModel.OutStartRunTime = ConfigInfoHelper.OutStartRunTime; BTModel.OutEndRunTime = ConfigInfoHelper.OutEndRunTime; vehicleio = new VehicleIO(BTModel); vehicleio.StartServer(); } /// <summary> /// 应用更改 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ApplyChanged_Click(object sender, EventArgs e) { //先停止监听线程 vehicleio.StopServer(); BTModel.InStartRunTime = dateTimePicker_InStart.Text; BTModel.InEndRunTime = dateTimePicker_InEnd.Text; BTModel.OutStartRunTime = dateTimePicker_OutStart.Text; BTModel.OutEndRunTime = dateTimePicker_OutEnd.Text; vehicleio = new VehicleIO(BTModel); vehicleio.StartServer(); }
VehicleIO.cs
private string InStartRunTime="";//园区进场开启时间 private string InEndRunTime = "";//园区进场结束时间 private string OutStartRunTime = "";//园区出场开启时间 private string OutEndRunTime = "";//园区出场结束时间 public VehicleIO(RunTimeModel btModel) { InStartRunTime = btModel.InStartRunTime; InEndRunTime = btModel.InEndRunTime; OutStartRunTime = btModel.OutStartRunTime; OutEndRunTime = btModel.OutEndRunTime; } private Thread thread = null; //初始值 private int sendLocationInterval = 3; //new 车辆进出园区数据模型 private VehicleIOModel iomodel = new VehicleIOModel(); /// <summary> /// 开始服务 /// </summary> public void StartServer() { ThreadStart iothread = new ThreadStart(working); thread = new Thread(iothread); thread.Start(); } /// <summary> /// 停止服务 /// </summary> public void StopServer() { if (thread != null && thread.IsAlive) { thread.Abort(); } } /// <summary> /// 挂起服务 /// </summary> public void SuspendServer() { if (thread.ThreadState == ThreadState.Running) { thread.Suspend(); } } /// <summary> /// 恢复服务 /// </summary> public void ResumeServer() { if (thread.ThreadState == ThreadState.Suspended) { thread.Resume(); } }
/// <summary> /// work. /// </summary> private void working() { MessagePrint.SendEventInfo(1, true, "园区进出场系统开启!"); while (true) { #region 进园区 if (DateTime.Now.ToLongTimeString().Equals(InStartRunTime)) { MessagePrint.SendEventInfo(1, true, "园区入场道口开启,车辆开始进入园区!"); DataTable dtVehicleInInfo = null; try { //获取未入场的卡号 dtVehicleInInfo = VehicleIODal.GetInstance().GetVehicleCardInInfo(); } catch (Exception exc) { MessagePrint.SendEventInfo(1, true, "未能查询到数据,请检查数据库连接串" + exc); } if (dtVehicleInInfo.Rows.Count > 0) { MessagePrint.SendEventInfo(1, true, "已查询到所有可以进入园区的车辆数据,正在准备进入园区……"); for (int i = 0; i < dtVehicleInInfo.Rows.Count; i++) { string cardNo = dtVehicleInInfo.Rows[i]["card_no"].ToString(); MessagePrint.SendEventInfo(1, true, "卡号为“" + cardNo + "”的车辆,正在进入园区……"); //这里记录进入信息...略 // //获取一个随机数(min 20,max 200,number 1) int rand = Convert.ToInt32(getRandom(20, 150, 1)); MessagePrint.SendEventInfo(1, true, "下一辆车辆将在" + sendLocationInterval * rand / 60 + "分钟后抵达园区"); if (DateTime.Now.ToLongTimeString().Equals(InEndRunTime) || DateTime.Now.ToLongTimeString().CompareTo(InEndRunTime) > 0) { MessagePrint.SendEventInfo(1, true, "园区入场道口关闭,停止车辆入场!入场道口将会在 " + InStartRunTime + " 再次开启!"); break; } else { Thread.Sleep(sendLocationInterval * 1000 * rand); } } MessagePrint.SendEventInfo(1, true, "注意:车辆进入园区操作完成!"); } else { MessagePrint.SendEventInfo(1, true, "目前没有查询到要进入园区的车辆。"); } } #endregion #region 出园区 //与进园区代码一致,略 #endregion } }
其他
显示实时时间
#region 运行时显示实时时间 new Thread(() => { while (true) { try { labelTime.BeginInvoke(new MethodInvoker(() => labelTime.Text = DateTime.Now.ToString())); } catch (Exception) { } Thread.Sleep(1000); } }) { IsBackground = true }.Start(); #endregion
随机数获取
/// <summary> /// 获取规定范围内的 n 个随机数 /// </summary> /// <param name="min_value">随机数下限</param> /// <param name="max_value">随机数上限</param> /// <param name="number">随机数量</param> /// <returns></returns> private string getRandom(int min_value, int max_value, int number) { Random random = new Random(); ArrayList arr = new ArrayList(); int temp = 0; for (int i = 0; i < number; i++) { temp = random.Next(min_value, max_value); //随机取数 arr.Add(temp); } string str = ""; for (int j = 0; j < arr.Count; j++) { str += arr[j].ToString() + ","; } return str.Substring(0, str.LastIndexOf(",")); }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: C# 文件操作
下一篇: 彻底找到 Tomcat 启动速度慢的元凶
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论