一切福田,不離方寸,從心而覓,感無不通。

Category Archives: C#

C#中的线程(三) 使用多线程

第三部分:使用多线程   1.  单元模式和Windows Forms 单元模式线程是一个自动线程安全机制, 非常贴近于COM——Microsoft的遗留下的组件对象模型。尽管.NET最大地放弃摆脱了遗留下的模型,但很多时候它也会突然出现,这是因为有必要与旧的API 进行通信。单元模式线程与Windows Forms最相关,因为大多Windows Forms使用或包装了长期存在的Win32 API——连同它的单元传统。 单元是多线程的逻辑上的“容器”,单元产生两种容量——“单的”和“多的”。单线 程单元只包含一个线程;多线程单元可以包含任何数量的线程。单线程模式更普遍 并且能与两者有互操作性。 就像包含线程一样,单元也包含对象,当对象在一个单元内被创建后,在它的生命周期中它将一直存在在那,永远也“居家不出”地与那些驻留线程在一起。这类似于被包含在.NET 同步环境中 ,除了同步环境中没有自己的或包含线程。任何线程可以访问在任何同步环境中的对象 ——在排它锁的控制中。但是单元内的对象只有单元内的线程才可以访问。 想象一个图书馆,每本书都象征着一个对象;借出书是不被允许的,书都在图书馆 创建并直到它寿终正寝。此外,我们用一个人来象征一个线程。 一个同步内容的图书馆允许任何人进入,同时同一时刻只允许一个人进入,在图书馆外会形成队列。 单元模式的图书馆有常驻维护人员——对于单线程模式的图书馆有一个图书管理员, 对于多线程模式的图书馆则有一个团队的管理员。没人被允许除了隶属与维护人员的人 ——资助人想要完成研究就必须给图书管理员发信号,然后告诉管理员去做工作!给管理员发信号被称为调度编组——资助人通过调度把方法依次读出给一个隶属管理员的人(或,某个隶属管理员的人!)。 调度编组是自动的,在Windows Forms通过信息泵被实现在库结尾。这就是操作系统经常检查键盘和鼠标的机制。如果信息到达的太快了,以致不能被处理,它们将形成消息队列,所以它们可以以它们到达的顺序被处理。   1.1  定义单元模式   .NET线程在进入单元核心Win32或旧的COM代码前自动地给单元赋值,它被默认地指定为多线程单元模式,除非需要一个单线程单元模式,就像下面的一样: 1 2 Thread t = new Thread (…); t.SetApartmentState (ApartmentState.STA); 你也可以用STAThread特性标在主线程上来让它与单线程单元相结合: 1 2 3 4 class Program {   [STAThread] static void Main() {   … 线程单元设置对纯.NET代码没有效果,换言之,即使两个线程都有STA 的单元状态,也可以被相同的对象同时调用相同的方法,就没有自动的信号编组或锁定发生了, 只有在执行非托管的代码时,这才会发生。 在System.Windows.Forms名称空间下的类型,广泛地调用Win32代码, 在单线程单元下工作。由于这个原因,一个Windos Forms程序应该在它的主方法上贴上 [STAThread]特性,除非在执行Win32 UI代码之前以下二者之一发生了: 它将调度编组成一个单线程单元 它将崩溃   1.2  Control.Invoke   在多线程的Windows Forms程序中,通过非创建控件的线程调用控件的的属性和方法是非法的。所有跨进程的调用必须被明确地排列至创建控件的线程中(通常为主线程),利用Control.Invoke 或 Control.BeginInvoke方法。你不能依赖自动调度编组因为它发生的太晚了,仅当执行刚好进入了非托管的代码它才发生,而.NET已有足够的时间来运行“错误的”线程代码,那些非线程安全的代码。 一个优秀的管理Windows Forms程序的方案是使用BackgroundWorker, 这个类包装了需要报道进度和完成度的工作线程,并自动地调用Control.Invoke方法作为需要。   1.3  BackgroundWorker   BackgroundWorker是一个在System.ComponentModel命名空间 下帮助类,它管理着工作线程。它提供了以下特性: "cancel" 标记,对于给工作线程打信号让它结束而没有使用 Abort的情况 提供报道进度,完成度和退出的标准方案 实现了IComponent接口,允许它参与Visual Studio设计器 […]

龙生   11 Sep 2016
View Details

C#中的线程(二) 线程同步基础

1.同步要领   下面的表格列展了.NET对协调或同步线程动作的可用的工具:                       简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程完成                       锁系统 构成 目的 跨进程? 速度 lock 确保只有一个线程访问某个资源或某段代码。 否 快 Mutex 确保只有一个线程访问某个资源或某段代码。可被用于防止一个程序的多个实例同时运行。 是 中等 Semaphore 确保不超过指定数目的线程访问某个资源或某段代码。 是 中等 (同步的情况下也提够自动锁。)                      信号系统 构成 目的 跨进程? 速度 EventWaitHandle 允许线程等待直到它受到了另一个线程发出信号。 是 中等 Wait 和 Pulse* 允许一个线程等待直到自定义阻止条件得到满足。 否 中等                          非阻止同步系统* 构成 目的 跨进程? 速度 Interlocked* 完成简单的非阻止原子操作。 是(内存共享情况下) 非常快 volatile* 允许安全的非阻止在锁之外使用个别字段。 非常快 * 代表页面将转到第四部分 1.1   阻止 (Blocking) 当一个线程通过上面所列的方式处于等待或暂停的状态,被称为被阻止。一旦被阻止,线程立刻放弃它被分配的 CPU时间,将它的ThreadState属性添加为WaitSleepJoin状态,不在安排时间直到停止阻止。停止阻止在任意四种 情况下发生(关掉电脑的电源可不算!): 阻止的条件已得到满足 操作超时(如果timeout被指定了) 通过Thread.Interrupt中断了 通过Thread.Abort放弃了 当线程通过(不建议)Suspend 方法暂停,不认为是被阻止了。 1.2  休眠 和 轮询 调用Thread.Sleep阻止当前的线程指定的时间(或者直到中断): 1 2 3 4 5 6 static void […]

龙生   11 Sep 2016
View Details

C#中的线程(一)入门

文章系参考转载,英文原文网址请参考:http://www.albahari.com/threading/ 作者 Joseph Albahari,  翻译 Swanky Wu 中文翻译作者把原文放在了"google 协作"上面,GFW屏蔽,不能访问和查看,因此我根据译文和英文原版整理转载到园子里面。 本系列文章可以算是一本很出色的C#线程手册,思路清晰,要点都有介绍,看了后对C#的线程及同步等有了更深入的理解。 入门 概述与概念 创建和开始使用多线程 线程同步基础 同步要领 锁和线程安全 Interrupt 和 Abort 线程状态 等待句柄 同步环境 使用多线程 单元模式和Windows Forms BackgroundWorker类 ReaderWriterLock类 线程池 异步委托 计时器 局部储存 高级话题 非阻止同步 Wait和Pulse Suspend和Resume 终止线程 一、入门 1.     概述与概念 C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行。一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为“主线程”)自动创建的,并具有多线程创建额外的线程。这里的一个简单的例子及其输出: 除非被指定,否则所有的例子都假定以下命名空间被引用了:  using System; using System.Threading; 1 2 3 4 5 6 7 8 9 10 11 class ThreadTest {   static void Main() {     Thread t = new Thread (WriteY);     t.Start();                          // Run WriteY on the new thread     while (true) Console.Write ("x");   // Write 'x' forever   }     static […]

龙生   11 Sep 2016
View Details

5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结

一、多线程带来的问题 1、死锁问题 前面我们学习了Task的使用方法,其中Task的等待机制让我们瞬间爱上了它,但是如果我们在调用Task.WaitAll方法等待所有线程时,如果有一个Task一直不返回,会出现什么情况呢?当然,如果我们不做出来的话,程序会一直等待下去,那么因为这一个Task的死锁,导致其他的任务也无法正常提交,整个程序"死"在那里。下面我们来写一段代码,来看一下死锁的情况:

  这里我们创建两个Task,t1和t2,t1里面有个while循环,由于条件一直为TRUE,所以他永远也无法退出。运行程序,结果如下: 可以看到Task2完成了,就是迟迟等不到Task1,这个时候我们按回车是没有反应的,除非关掉窗口。如果我们在项目中遇到这种情况是令人很纠结的,因为我们也不知道到底发生了什么,程序就是停在那里,也不报错,也不继续执行。 那么出现这种情况我们该怎么处理呢?我们可以设置最大等待时间,如果超过了等待时间,就不再等待,下面我们来修改代码,设置最大等待时间为5秒(项目中可以根据实际情况设置),如果超过5秒就输出哪个任务出错了,代码如下:

  这里我们将所有任务放到一个数组里面进行管理,调用Task.WaitAll的一个重载方法,第一个参数是Task[]数据,第二个参数是最大等待时间,单位是毫秒,这里我们设置为5000及等待5秒钟,就继续向下执行。下面我们遍历Task数组,通过Status属性判断哪些Task没有完成,然后输出错误信息。 2、SpinLock(自旋锁) 我们初识多线程或者多任务时,第一个想到的同步方法就是使用lock或者Monitor,然而在4.0 之后微软给我们提供了另一把利器——spinLock,它比重量级别的Monitor具有更小的性能开销,它的用法跟Monitor很相似,VS给的提示如下: 下面我们来写一个例子看一下,代码如下(关于lock和Monitor的用法就不再细说了,网上资料很多,大家可以看看):

  输出结果如图: 这里我们使用了Parallel.For方法来做演示,Parallel.For用起来方便,但是在实际开发中还是尽量少用,因为它的不可控性太高,有点简单粗暴的感觉,可能带来一些不必要的"麻烦",最好还是使用Task,因为Task的可控性较好。 slock.Enter方法,解释如下: 3、多线程之间的数据同步 多线程间的同步,在用thread的时候,我们常用的有lock和Monitor,上面刚刚介绍了.Net4.0中一个新的锁——SpinLock(自旋锁),实际上,我们还可以将任务分成多块,由多个线程一起执行,最后合并多个线程的结果,如:求1到100的和,我们分10个线程,分别求1~10,……,90~100的和,然后合并十个线程的结果。还有就是使用线程安全集合,可参加第二天的文章。其实Task的同步机制做已经很好了,如果有特殊业务需求,有线程同步问题,大家可一起交流~~ 二、Task和线程池之间的抉择 我们要说的task的知识也说的差不多了,接下来我们开始站在理论上了解下“线程池”和“任务”之间的关系,我们要做到知其然,还要知其所以然。不管是说线程还是任务,我们都不可避免的要讨论下线程池,然而在.net 4.0以后,线程池引擎考虑了未来的扩展性,已经充分利用多核微处理器架构,只要在可能的情况下,我们应该尽量使用task,而不是线程池。 这里简要的分析下CLR线程池,其实线程池中有一个叫做“全局队列”的概念,每一次我们使用QueueUserWorkItem的使用都会产生一个“工作项”,然后“工作项”进入“全局队列”进行排队,最后线程池中的的工作线程以FIFO(First Input First Output)的形式取出,这里值得一提的是在.net 4.0之后“全局队列”采用了无锁算法,相比以前版本锁定“全局队列”带来的性能瓶颈有了很大的改观。那么任务委托的线程池不光有“全局队列”,而且每一个工作线程都有”局部队列“。我们的第一反应肯定就是“局部队列“有什么好处呢?这里暂且不说,我们先来看一下线程池中的任务分配,如下图: 线程池的工作方式大致如下,线程池的最小线程数是6,线程1~3正在执行任务1~3,当有新的任务时,就会向线程池请求新的线程,线程池会将空闲线程分配出去,当线程不足时,线程池就会创建新的线程来执行任务,直到线程池达到最大线程数(线程池满)。总的来说,只有有任务就会分配一个线程去执行,当FIFO十分频繁时,会造成很大的线程管理开销。 下面我们来看一下task中是怎么做的,当我们new一个task的时候“工作项”就会进去”全局队列”,如果我们的task执行的非常快,那么“全局队列“就会FIFO的非常频繁,那么有什么办法缓解呢?当我们的task在嵌套的场景下,“局部队列”就要产生效果了,比如我们一个task里面有3个task,那么这3个task就会存在于“局部队列”中, 如下图的任务一,里面有三个任务要执行,也就是产生了所谓的"局部队列",当任务三的线程执行完成时,就会从任务一种的队列中以FIFO的形式"窃取"任务执行 ,从而减少了线程管理的开销。这就相当于,有两个人,一个人干完了分配给自己的所有活,而另一个人却还有很多的活,闲的人应该接手点忙的人的活,一起快速完成。 从上面种种情况我们看到,这些分流和负载都是普通ThreadPool.QueueUserWorkItem所不能办到的,所以说在.net 4.0之后,我们尽可能的使用TPL,抛弃ThreadPool。 这是5天玩转C#并行和多线程编程系列的最后一篇了,当然还有很多东西没说到,如果真的想要玩转多线程,还是要多多努力学习的。大家在学习过程中有什么问题可以一起交流~~ 如果大家感觉我的博文对大家有帮助,请推荐支持一把,给我写作的动力。 from:http://www.tuicool.com/articles/2maqYrI

龙生   11 Sep 2016
View Details

c#使用多线程的几种方式示例详解

(1)不需要传递参数,也不需要返回参数 ThreadStart是一个委托,这个委托的定义为void ThreadStart(),没有参数与返回值。 复制代码代码如下: class Program{ static void Main(string[] args) { for (int i = 0; i < 30; i++) { ThreadStart threadStart = new ThreadStart(Calculate); Thread thread = new Thread(threadStart); thread.Start(); } Thread.Sleep(2000); Console.Read(); } public static void Calculate() { DateTime time = DateTime.Now;//得到当前时间 Random ra = new Random();//随机数对象 Thread.Sleep(ra.Next(10,100));//随机休眠一段时间 Console.WriteLine(time.Minute + ":" + time.Millisecond); } } (2)需要传递单个参数 ParameterThreadStart委托定义为void ParameterizedThreadStart(object state),有一个参数但是没有返回值。 复制代码代码如下: class Program{ static void Main(string[] args) { for (int i = 0; i < 30; i++) { ParameterizedThreadStart tStart = new ParameterizedThreadStart(Calculate); Thread thread = […]

龙生   11 Sep 2016
View Details

Asp.net+Mysql,查询出错:由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败

在网上看到的解决方案基本都是说host文件中127.0.0.1 localhost 的对应, 但我查看了host文件,并没有问题,纠结半天,拿查询语句在Mysql Workbench上执行,报了一个错,说是子查询包含多个结果,我去~!这很简单的一个错误asp.net竟然没能准确反馈。 于是在子查询语句后面加limit 1,Ok~ 所以,出现“由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败”这个错误时,有可能是查询语句有问题,而不是连接有问题。 from:http://blog.csdn.net/idoiknow/article/details/8923553

龙生   07 Sep 2016
View Details

NPOI读写Excel

1、整个Excel表格叫做工作表:WorkBook(工作薄),包含的叫页(工作表):Sheet;行:Row;单元格Cell。 2、NPOI是POI的C#版本,NPOI的行和列的index都是从0开始 3、POI读取Excel有两种格式一个是HSSF,另一个是XSSF。 HSSF和XSSF的区别如下: HSSF is the POI Project’s pure Java implementation of the Excel ’97(-2007) file format. XSSF is the POI Project’s pure Java implementation of the Excel 2007 OOXML (.xlsx) file format. 即:HSSF适用2007以前的版本,XSSF适用2007版本及其以上的。 下面是用NPOI读写Excel的例子:ExcelHelper封装的功能主要是把DataTable中数据写入到Excel中,或者是从Excel读取数据到一个DataTable中。 ExcelHelper类:

测试代码:

Excel相关DLL下载:NPOI-Lib.rar   1.NPOI下载地址:http://npoi.codeplex.com/releases/view/38113 2.NPOI学习系列教程推荐:http://www.cnblogs.com/tonyqus/archive/2009/04/12/1434209.html   参考: http://www.cnblogs.com/Erik_Xu/archive/2012/06/08/2541957.html http://www.cnblogs.com/linzheng/archive/2010/12/20/1912137.html http://www.cnblogs.com/knowledgesea/archive/2012/11/16/2772547.html   from:http://www.cnblogs.com/luxiaoxun/p/3374992.html

龙生   06 Sep 2016
View Details

如何在EF中直接运行SQL命令

相信不少使用EF的同志们已经知道如何在EF中运行SQL命令了。我在这里简单总结下,希望对大家学习EF有所帮助! 在 EF第一个版本(.NET 3.5 SP1)中,我们只能通过将ObjectContext.Connection转换为EntityConnection,再把 EntityConnection.StoreConnection转换为SqlConnection。有了这个SqlConnection,我们再创建 SqlCommand便能顺利运行SQL命令了。(个人觉得其实很烦,呵呵) 例如: EntityConnection entityConnection = (EntityConnection)ctx.Connection; DbConnection storeConnection = entityConnection.StoreConnection; DbCommand cmd = storeConnection.CreateCommand(); cmd.CommandType = System.Data.CommandType.StoredProcedure; cmd.CommandText = "[PRO_USER_DIGITALCARD_CHECK]"; 。。。。。。。 在EF4(.NET 4)中,我们有了全新的API:ObjectContext.ExecuteStoreCommand(…)和 ObjectContext.ExecuteStoreQuery<T>(…)。从函数名不难知道前者是为了执行某一并无返回集的SQL 命令,例如UPDATE,DELETE操作;后者是执行某一个查询,并可以将返回集转换为某一对象。 using (var ctx = new MyObjectContext()) { ctx.ExecuteStoreCommand("UPDATE Person SET Name = 'Michael' WHERE PersonID = 1"); } using (var ctx = new MyObjectContext()) { var peopleViews = ctx.ExecuteStoreQuery<PersonView>("SELECT PersonID, Name FROM Person"); } public class PersonView { public int PersonID { get; set; } public string Name { get; set; } } 现在有了EF4.1,API的名字又有了些许改变。如果说DbContext将ObjectContext做了包装,那么DbContext.Database就是对应于数据库端信息的封装。执行SQL命令也自然从Database类型开始。对应于ExecuteStoreCommand和ExecuteStoreQuery<T>的是Database.ExecuteSqlCommand和Database.SqlQuery<T>。 using (var ctx = new MyDbContext()) { ctx.Database.ExecuteSqlCommand("UPDATE Person SET Name = 'Michael' WHERE PersonID = 1"); } using (var ctx = new MyDbContext()) { var peopleViews = ctx.SqlQuery<PersonView>("SELECT PersonID, Name FROM Person").ToList(); } public class PersonView { public int PersonID { get; set; } public string Name { get; set; } } from:http://www.cnblogs.com/chengxiaohui/articles/2092001.html

龙生   03 Sep 2016
View Details

ADO.NET ENTITY FRAMEWORK : (二十) 提升EF执行速度方法

这边分享一下,提升ADO.Net Entity Framework执行速度的几个方法, 技巧一 取得单一数据时,可利用【GetObjectByKey】 在预设中,ObjectContext 在查询数据的时候,并不会以快取对象作为优先查询, 会在每次查询时都向数据库要数据,对效能会产生一定的影响, 因此可以利用【GetObjectByKey】方法来对快取对象进行查询, 但如果使用【GetObejctByKey】方法进行查询,而快取对象又不存在时, 会发生 ObjectNotFoundException 例外, 要避免此问题可利用【TryGetObejctByKey】方法, 如果在快取对象中找不到数据时,会回传 false,

技巧二  重复执行相同查询语法,可利用已编译查询【CompiledQuery】 在我们每次下达 Linq to Entities 向数据库查询数据时, 会先编译成 Entity SQL Language ,再经由 Provider 转换成对应该数据库的语法, 如果我们在循环里,对同一个查询下达1000次的话,就要经过1000次的转换, 这是很浪费效能的, 因此 ADO.Net Entity Framework 提供了 CompiledQuery 类别, 可以进行查询的编译和快取以供重复使用, 这边简单看一个范例

技巧三  只进行查询,而不异动数据时 可利用【MergeOption.NoTracking】 使用【MergeOption.NoTracking】时, 因为不会再 ObjectStateManager 中追踪对象异动状态, 因此查询时效能较好,(系统默认是使用【MergeOption.AppendOnly】) 范例

  参考连结 CompiledQuery 类别 已编译的查询 (LINQ to Entities) MergeOption 列举型别 ObjectStateManager ObjectContext..::.TryGetObjectByKey 方法   from:http://it.zhaozhao.info/archives/16465

龙生   03 Sep 2016
View Details
1 23 24 25 47