权威NET多线程详解

上传人:悦** 文档编号:171266593 上传时间:2022-11-25 格式:DOCX 页数:19 大小:278.01KB
收藏 版权申诉 举报 下载
权威NET多线程详解_第1页
第1页 / 共19页
权威NET多线程详解_第2页
第2页 / 共19页
权威NET多线程详解_第3页
第3页 / 共19页
资源描述:

《权威NET多线程详解》由会员分享,可在线阅读,更多相关《权威NET多线程详解(19页珍藏版)》请在装配图网上搜索。

1、目录本文档网上收集,如果出现版权问题,请及时的联系我,我将删除之!1基础篇. 怎样创建一个线程 受托管的线稈与Windows线程 前台线程与后台线程 名为BeginXXX和EndXXX的方法是做什么用的 异步和多线程有什么关联WinForm多线程编程篇 我的多线程 WinForm程序老是抛出 InvalidOperationException,怎么解决? Invoke, BeginInvoke 干什么用的,内部是怎么实现的 每个线程都有消息队列吗? 为什么 Winform不允许跨线程修改UI线程控件的值 有没有什么办法可以简化WinForm多线程的开发线程池 线程池的作用是什么? 所有进程使用

2、一个共享的线程池,还是每个进程使用独立的线程池? 为什么不要手动线程池设置最大值? .Net线程池有什么不足?同步 CLR怎样实现lock(obj)锁定? WaitHandle是什么.他和他的派牛类怎么使用 什么是用双锁实现Singleton.为什么要这样做,为什么有人说双锁检验是不安全的 互斥对象(Mutex)、事件(Event) 对象与 lock 语句的比较什么时候需要锁定 只有共享资源才需要锁定 把锁定交给数据库 了解你的程序是怎么运行的业务逻辑对事务和线程安全的要求 计算一下冲突的可能性 请多使用 lock.少用 MutexWeb 和 IIS 应用程序池-WebApplication和

3、 线程池之间有什么关系 Web 页面怎么调用异步 WebService基础篇怎样创建一个线程)使用Thread类ThreadStart threadStart=new ThreadStart(Calculate);/通过 ThreadStart 委托告诉子线程讲执行什么 方法,这里执行一个计算圆周长的方法Thread thread=new Thread(threadStart);thread.Start(); 启动新线程public void Calculate()double Diameter=0.5;Console.Write(The perimeter Of Circle with a

4、Diameter of 0 is lDiameter,Diameter*Math.PI);)使用 Delegate.BeginInvokedelegate double CalculateMethod(double Diameter); 申明一个委托,表明需要在子线程上执行 的方法的函数签名static CalculateMethod calcMethod = new CalculateMethod(Calculate);/ 把委托和具体的方法关 联起来static void Main(string args)此处开始异步执行,并且可以给出一个回调函数(如果不需要执行什么后续操作也可以不使 用

5、回调)calcMethod.BeginInvoke(5, new AsyncCallback(TaskFinished), null);Console.ReadLine();线程调用的函数,给出直径作为参数,计算周长public static double Calculate(double Diameter)return Diameter * Math.PI;线程完成之后回调的函数public static void TaskFinished(IAsyncResult result)double re = 0;re = calcMethod.EndInvoke(result);Console.

6、WriteLine(re);三)使用 ThreadPool.QueueworkItemWaitCallback w = new WaitCallback(Calculate); 下面启动四个线程,计算四个直径下的圆周长 ThreadPool.QueueUserWorkItem(w, 1.0); ThreadPool.QueueUserWorkItem(w, 2.0); ThreadPool.QueueUserWorkItem(w, 3.0); ThreadPool.QueueUserWorkItem(w, 4.0);public static void Calculate(double Dia

7、meter)return Diameter * Math.PI;下面两条来自于 html受托管的线程与Windows线程必 须要了解,执行.NET应用的线程实际上仍然是Windows线程。但是,当某个 线程被CLR所知时,我们将它称为受托管的线程。具体来说,由受托管的代 码 创建出来的线程就是受托管的线程。如果一个线程由非托管的代码所创建,那么 它就是非托管的线程。不过,一旦该线程执行了受托管的代码它就变成了受托管 的线程。一个受托管的线程和非托管的线程的区别在于,CLR将创建一个System.Threading.Thread类的实例来代表并操作前者。在内部实现中,CLR将 一个包含了所有受托

8、管线程的列表保存在一个叫做Threads tore地方。CLR确保每一个受托管的线程在任意时刻都在一个AppDomain中执行,但是这并 不代表一个线程将永远处在一个AppDomain中,它可以随着时间的推移转到其他 的 AppDomain 中。从安全的角度来看,一个受托管的线程的主用户与底层的非托管线程中的 Windows主用户是无关的。前台线程与后台线程启动了多个线程的程序在关闭的时候却出现了问题,如果程序退出的时候不关闭线程,那 么线程就会一直的存在,但是大多启动的线程都是局部变量,不能一一的关闭,如果调用 Thread.CurrentThread.Abort()方法关闭主线程的话,就会

9、出现 ThreadAbortException 异常, 因此这样不行。后来找到了这个办法:Thread.IsBackground设置线程为后台线程。msdn对前台线程和后台线程的解释:托管线程或者是后台线程,或者是前台线程。后台线 程不会使托管执行环境处于活动状态,除此之外,后台线程与前台线程是一样的。一旦所有 前台线程在托管进程(其中.exe文件是托管程序集)中被停止,系统将停止所有后台线程 并关闭。通过设置Thread.IsBackground属性,可以将一个线程指定为后台线程或前台线程。 例如,通过将Thread.IsBackground设置为true,就可以将线程指定为后台线程。同样,

10、通 过将IsBackground设置为false,就可以将线程指定为前台线程。从非托管代码进入托管执 行环境的所有线程都被标记为后台线程。通过创建并启动新的Thread对象而生成的所有线 程都是前台线程。如果要创建希望用来侦听某些活动(如套接字连接)的前台线程,则应将 Thread.IsBackground设置为true,以便进程可以终止。所以解决办法就是在主线程初始化的时候,设置:Thread.CurrentThread.IsBackground = true;这样,主线程就是后台线程,在关闭主程序的时候就会关闭主线程,从而关闭所有线程。但 是这样的话,就会强制关闭所有正在执行的线程,所以在

11、关闭的时候要对线程工作的结果保 存。经常看到名为BeginXXX和EndXXX的方法,他们是做什么用的 这是.net的一个异步方法名称规范 Net在设计的时候为异步编程设计了一个异步编程模型(APM),这个模型不仅 是使用.NET的开发人员使用,.Net内部也频繁用到,比如所有的St ream就有 BeginRead, EndRead, Socket,WebRequet,SqlCommand 都运用到了这个模式,一 般来讲,调用BegionXXX的时候,一般会启动一个异步过程去执行一个操作, EndEnvoke可以接收这个异步操作的返回,当然如果异步操作在EndEnvoke调用 的时候还没有执

12、行完成,EndInvoke会一直等待异步操作完成或者超时.Net 的异步编程模型(APM) 般包含 BeginXXX,EndXXX,IAsyncResult 这三 个元素,BeginXXX方法都要返回一个IAsyncResult,而EndXXX都需要接收一个 IAsyncResult作为参数,他们的函数签名模式如下IAsyncResult BeginXXX();返回类型EndXXX(IAsyncResult ar);BeginXXX和EndXXX中的XXX,一般都对应一个同步的方法,比如FileStream的 Read方法是一个同步方法,相应的BeginRead(), EndRead ()就是

13、他的异步版本, Htt pRequest 有 Get Response 来同步接收一个响应,也提供了 BeginGe tResponse 和EndGetResponse这个异步版本,而IAsynResult是二者联系的纽带,只有把 BeginXXX所返回的IAsyncResult传给对 应的EndXXX,EndXXX才知道需要去接 收哪个BeginXXX发起的异步操作的返回值。这个模式在实际使用时稍显繁琐,虽然原则上我们可以随时调用EndInvoke来 获得返回值,并且可以同步多个线程,但是大多数情况下当我们不需要同步很多 线程的时候使用回调是更好的选择,在这种情况下三个元素中的IAsynRe

14、sult 就显得多余,我们一不需要用其中的线程完结标志来判断线程是否成功完成(回 调的时候线程应该已经完成了),二不 需要他来传递数据,因为数据可以写在任 何变量里,并且回调时应该已经填充,所以可以看到微软在新的.Net Framework 中已经加强了对回调事件的支持,这总模型下,典型的回调程序应该这样写a.DoWork+二new SomeEve nt Handler(Cacula te);a.CallBack+二new SomeEve nt Handler(callback);a.Run();(注:我上面讲的是普遍的用法,然而BeginXXX,EndXXX仅仅是一种模式,而 对这个模式的实

15、现完全取决于使用他的开发人员,具体实现的时候你可以使用 另外一个线程来实现异步,也可能使用硬件的支持来实现异步,甚至可能根本和 异步没有关系(尽管几乎没有人会这样做)比如直接在Beginxxx里直接输出一个Helloworld,如果是这种极端的情况,那么上面说的一切都是废话, 所以上面的探讨并不涉及内部实现,只是告诉大家微软的模式,和框架中对这 个模式的经典实现)异步和多线程有什么关联有一句话总结的很好:多线程是实现异步的一种手段和工具我们通常把多线程和异步等同起来,实际是一种误解,在实际实现的时候,异步 有许多种实现方法,我们可以用进程来做异步,或者使用纤程,或者硬件的一些 特性,比如在实现

16、异步IO的时候,可以有下面两个方案:1)可以通过初始化一个子线程,然后在子线程里进行IO,而让主线程顺利往下 执行,当子线程执行完毕就回调2)也可以根本不使用新线程,而使用硬件的支持(现在许多硬件都有自己的处理 器),来实现完全的异步,这是我们只需要将IO请求告知硬件驱动程序,然后 迅速返回,然后等着硬件IO就绪通知我们就可以了实 际上DotNet Framework里面就有这样的例子,当我们使用文件流的时候,如 果制定文件流属性为同步,则使用BeginRead进行读取时,就是用一个子线程来 调用同步的Read方法,而如果指定其为异步,则同样操作时就使用了需要硬件 和操作系统支持的所谓IOCP

17、的机制WinForm多线程编程篇我的多线程WinForm程序老是抛出InvalidOperationException ,怎么解决?在WinForm中使用多线程时,常常遇到一个问题,当在子线程(非UI线程)中 修改一个控件的值:比如修改进度条进度,时会抛出如下错误Cross-thread operation not valid: Control XXX accessed from a thread other than the thread it was created on.在VS2005或者更高版本中,只要不是在控件的创建线程(一般就是指UI主线 程)上访问控件的属性就会抛出这个错误,解决

18、方法就是利用控件提供的Invoke 和BeginInvoke把调用封送回UI线程,也就是让控件属性修改在UI线程上执 行,下面列出会报错的代码和他的修改版本ThreadStart threadStart=new ThreadStart(Calculate);/通过 ThreadStart 委托告诉子线程讲执行什么 方法Thread thread=new Thread(threadStart);thread.Start();public void Calculate()double Diameter=0.5;double result=Diameter*Math.PI;CalcFinished(

19、result);/计算完成需要在一个文本框里显示public void CalcFinished( double result)this. TextBox 1 Text=result.ToString()会抛出错误上面加粗的地方在debug的时候会报错,最直接的修改方法是修改Calculate 这个方法如下delegate void changeText(double result);public void Calculate()double Diameter=0.5;double result=Diameter*Math.PI;this.BeginInvoke(new changeText(

20、CalcFinished),t.Result); 计算完成需要在一个文本框里显 示这样就ok 了,但是最漂亮的方法是不去修改Calculate,而去修改CalcFinished 这个方法,因为程序里调用这个方法的地方可能很多,由于加了是否需要封送的 判断,这样修改还能提高非跨线程调用时的性能delegate void changeText(double result);public void CalcFinished( double result) if(this.InvokeRequired)this.BeginInvoke(new changeText(CalcFinished),t.Re

21、sult); elsethis.TextBoxl.Text=result.ToString();上面的做法用到了 Control的一个属性InvokeRequired (这个属性是可以在其 他线程里访问的),这个属性表明调用是否来自另非UI线程,如果是,则使用 BeginInvoke来调用这个函数,否则就直接调用,省去线程封送的过程Invoke,BeginInvoke干什么用的,内部是怎么实现的?这两个方法主要是让给出的方法在控件创建的线程上执行Invoke 使用了 Win32API 的 SendMessage,UnsafeNativeMethodsPostMessage(new Handle

22、Ref(this, this.Handle),threadCallbackMessage, IntPtr. Zero, IntPtr. Zero);BeginInvoke 使用了 Win32API 的 PostMessageUnsafeNativeMethodsPostMessage(new HandleRef(this, this.Handle),threadCallbackMessage, IntPtr. Zero, IntPtr. Zero);这两个方法向UI线程的消息队列中放入一个消息,当UI线程处理这个消息时, 就会在自己的上下文中执行传入的方法,换句话说凡是使用BeginInvok

23、e和 Invoke调用的线程都是在UI主线程中执行的,所以如果这些方法里涉及一些静 态变量,不用考虑加锁的问题每个线程都有消息队列吗?不是,只有创建了窗体对象的线程才会有消息队列(下面给出Windows核心编程关于这一段的描述)当一个线程第一次被建立时,系统假定线程不会被用于任何与用户相关的任务。这样可以减少 线程对系统资源的要求。但是,一旦这个线程调用一个与图形用户界面有的函数(例如检查 它的消息队列或建立一个窗口)系统就会为该线程分配一些另外的资源以便它能够执行与用 户界面有关的任务。特别是,系统分配一个H R E A DIN F结构,并将这个数据结构与线 程联系起来。这个T H R E

24、A D I N F C结构包含一组成员变量,利用这组成员,线程可以认为它是在自己 独占的环境中运行。T H R E A D I N F 是一个内部的、未公开的数据结构,用来指定线程的 登记消息队列posted-message queud、发送消息队列(send-message queue、应答消息 队列(r e p l y -message queue、虚拟输入队列 Cirtualized-input queue、唤醒标志(wake flag)、以及用来描述线程局部输入状态的若干变量。图6 - 1描述了T H R E A D I N F O结构和与之相联系的三个线程。红记消息队列搞钉-虚拟输入

25、iu列指豺r览送消息从列指針-应答消息除列指針-ExLtCode唤阴标士局都输入状赢蛮呈一1THREADINFO辽记消息队列拒在虚拟输入从列指針岌送消总臥列播针-应答消息从列描针nExjtCod咲駆松忐局部输入状态变甲丨THREADINFO登记消息肚列指针发送消息队见抬钊应答MSGJ- _应輕旳)THREADINFO虚拟输入队耻抬钊应答消息从列指針nExLtC ode唤醒标志扃部输入状态蛮量-登记的卜岱才 登记的翌sg_发送MSO J-塩送MSGJ- MSGj-应答M呂G为什么Winform不允许跨线程修改UI线程控件的值在vs2003下,使用子线程调用ui线程创建的控件的属性是不会有问题的,

26、但是编 译的时候会出现警告,但是vs2005及以上版本就会有这样的问题,下面是msdn 上的描述当您在Visual Studio调试器中运行代码时,如果您从一个线程访问某个UI 元素,而该线程不是创建该UI元素时所在的线程,则会引发InvalidOperationExcep tion。调试器引发该异常以警告您存在危险的编程操作。UI元素不是线程安全的,所以只应在创建它们的线程上进行访问从上面可以看出,这个异常实际是debugger耍的花招,也就是说,如果你直接运 行程序的exe文件,或者利用运行而不调试(Ctrl+F5)来运行你的程序,是不会 抛出这样的异常的.大概ms发现v2003的警告对广

27、大开发者不起作用,所以用了 一个比较狠一点的方法.不过问题依然存在:既然这样设计的原因主要是因为控件的值非线程安全,那么 Dot Net framework中非线程安全的类千千万万,为什么偏偏跨线程修改Cont rol 的属性会有这样严格的限制策略呢?这个问题我还回答不好,希望博友们能够予以补充有没有什么办法可以简化WinForm多线程的开发使用backgroundworker,使用这个组建可以避免回调时的Invoke和BeginInvoke, 并且提供了许多丰富的方法和事件参见.Net多线程总结(二)Backgroundworker,我在这里不再赘诉线程池线程池的作用是什么作用是减小线程创建

28、和销毁的开销创建线程涉及用户模式和内核模式的切换,内存分配,dll通知等一系列过程, 线程销毁的步骤也是开销很大的,所以如果应用程序使用了完一个线程,我们能 把线程暂时存放起来,以备下次使用,就可以减小这些开销所有进程使用一个共享的线程池,还是每个进程使用独立的线程池?每 个进程都有一个线程池,一个Process中只能有一个实例,它在各个应用程序 域(AppDomain)是共享的,.Net2.0中默认线程池的大小为工作线程25个,10 线程1000个,有一个比较普遍的误解是线程池中会有1000个线程等着你去取, 其实不然,ThreadPool仅仅保留相当少的线程,保留的线程可以用SetMinT

29、hread 这个方法来设置,当程序的某个地方需要创建一个线程来完成工作时,而线程 池中又没有空闲线程时,线程池就会负责创建这个线程,并且在调用完毕后,不会 立刻销毁,而是把他放在池子里,预备下次使用,同时如果线程超过一定时间没 有被使用,线程池将会回收线程,所以线程池里存在的线程数实际是个动态的过 程为什么不要手动线程池设置最大值?当我首次看到线程池的时候,脑袋里的第一个念头就是给他设定一个最大值,然 而当我们查看ThreadPool的SetMaxThreads文档时往往会看到一条警告:不要手 动更改线程池的大小,这是为什么呢?其 实无论FileStream的异步读写,异步发送接受Web请求,

30、甚至使用delegate 的beginlnvoke都会默认调用ThreadPool,也就是说不仅你的代码可能使用到 线程池,框架内部也可能使用到,更改的后果影响就非常大,特别在iis中,一个 应用程序池中的所有WebApplication会共享一个线程池,对最大值的设定会带 来很多意想不到的麻烦线程池的线程为何要分类?线 程池有一个方法可以让我们看到线程池中可用的线程数量:Ge tAvaliableThread(o ut workerThreadCou nt,out iocompletedThreadCount),对于我来说,第一次看到这个函数的参数时十分困惑, 因为我期望这个函数直接返回一个

31、整形,表明还剩多少 线程,这个函数居然一次 返回了两个变量.原来线程池里的线程按照公用被分成了两大类:工作线程和IO线程,或者IO完成 线程,前者用于执行普通的操作,后者专用于异步IO,比如文件和网络请求,注 意,分类并不说明两种线程本身有差别,线程就是线程,是一种执行单元,从本质 上来讲都是一样 的,线程池这样分类,举例来说,就好像某施工工地现在有1000 把铁锹,规定其中25把给后勤部门用,其他都给施工部门,施工部门需要大量使 用铁锹来挖地 基(例子土了点,不过说明问题还是有效的),后勤部门用铁锹也就 是铲铲雪,铲铲垃圾,给工人师傅修修临时住房,所以用量不大,显然两个部门的 铁锹本身没有区

32、别,但是这样的划分就为管理两个部门的铁锹提供了方便线程池中两种线程分别在什么情况下被使用,二者工作原理有什么不同?下面这个例子直接说明了二者的区别,我们用一个流读出一个很大的文件(大一 点操作的时间长,便于观察),然后用另一个输出流把所读出的文件的一部分写到 磁盘上 我们用两种方法创建输出流,分别是创建了一个异步的流(注意构造函数最后那个true)FileStream outputfs二new FileStream(writepath, FileMode.Create, FileAccess.Wr ite, FileShare.None,256,t rue);创建了一个同步的流FileStre

33、am outputfs = File.OpenWrite(writepath);然后在写文件期间查看线程池的状况string readpath = e:RHEL4-U4-i386-AS-discl.iso;string writepath = e:kakakak.iso;byte buffer = new byte9OOOOOOO;/FileStream outputfs=new FileStream(writepath, FileMode.Create, FileAccess.Write, FileShare.None, 256,true);Console.WriteLine(异步流”);创

34、建了一个同步的流FileStream outputfs = File.OpenWrite(writepath);Console.WriteLine(同步流”);然后在写文件期间查看线程池的状况ShowThreadDetail(初始状态”);FileStream fs = File.OpenRead(readpath);fs.BeginRead(buffer, 0, 90000000, delegate(IAsyncResult o)outputfs.BeginWrite(buffer, 0, buffer.Length,delegate(IAsyncResult o1)Thread.Sleep

35、(1000);ShowThreadDetail(BeginWrite 的回调线程”);, null);Thread.Sleep(500);/this is important cause without this, this Thread and the one used for BeginRead May seem to be same one, null);Console.ReadLine();public static void ShowThreadDetail(string caller)int IO;int Worker;ThreadPool.GetAvailableThreads(

36、out Worker, out IO);Console.WriteLine(Worker: 0; IO: 1, Worker, IO); 输出结果异步流Worker:500;10:1000Worker:500;10:999同步流Worker:500;I0:1000Worker:499;I0:1000这两个构造函数创建的流都可以使用BeginWrite来异步写数据,但是二者行为 不同,当使用同步的流进行异步写时,通过回调的输出我们可以看到,他使用的是 工作线程,而非IO线程,而异步流使用了 IO线程而非工作线程其 实当没有制定异步属性的时候,.Net实现异步IO是用一个子线程调用fs的 同步Wr

37、ite方法来实现的,这时这个子线程会一直阻塞直到调用完成.这个子 线 程其实就是线程池的一个工作线程,所以我们可以看到,同步流的异步写回调中 输出的工作线程数少了一,而使用异步流,在进行异步写时,采用了 IOCP方法, 简单说来,就是当BeginWrite执行时,把信息传给硬件驱动程序,然后立即往下 执行(注意这里没有额外的线程),而当硬件准备就绪,就会通知线程池,使用一 个IO线程来读取.Net线程池有什么不足 没有提供方法控制加入线程池的线程:一旦加入线程池,我们没有办法挂起,终止 这些线程,唯一可以做的就是等他自己执行1)不能为线程设置优先级2)个Process中只能有一个实例,它在各个

38、AppDomain是共享的。ThreadPool 只提供了静态方法,不仅我们自己添加进去的WorkI tem使用这个Pool,而且.net framework中那些BeginXXX、EndXXX之类的方法都会使用此Pool。3)所支持的Callback不能有返回值。WaitCallback只能带一个object类型的 参数,没有任何返回值。4)不适合用在长期执行某任务的场合。我们常常需要做一个Service来提供不间 断的服务(除非服务器down掉),但是使用ThreadPool并不合适。下面是另外一个网友总结的什么不需要使用线程池,我觉得挺好,引用下来 如果您需要使一个任务具有特定的优先级。

39、如果您具有可能会长时间运行(并因此阻塞其他任务)的任务。如果您需要将线程放置到单线程单元中所有ThreadPool线程均处于多线程单 元中)。如果您需要与该线程关联的稳定标识例如,您应使用一个专用线程来中止该线 程、将其挂起或按名称发现它。锁定与同步CLR怎样实现lock(obj)锁定?从原理上讲,lock和Syncronized Attribute都是用Moniter.Enter实现的,比 如如下代码object lockobj=new object。; lock(obj)/do things在编译时,会被编译为类似tryMoniter.Enter(obj)/do thingscatchfi

40、nallyMoniter.Exit(obj);而MethodImpl(MethodImplOptions.Synchronized)标记为同步的方法会在编译时被lock(this)语句所环绕所以我们只简单探讨Moni ter.E nt er的实现注:DotNet 并非使用 Win32API 的 CriticalSection 来实现 Moniter.Enter, 不过他为托管对象提供了一个类似的结构叫做Syncblk)每 个对象实例头部都有一个指针,这个指针指向的结构,包含了对象的锁定信息, 当第一次使用Moniter.Enter(obj)时,这个obj对象的锁定结 构就会被初时化, 第二次调

41、用Moniter.Enter时,会检验这个object的锁定结构,如果锁没有被释 放,则调用会阻塞Fin*m*r QuueWaitHandle是什么他和他的派生类怎么使用WaitHandle 是 Mutex, Semaphore, EventWaitHandler, AutoResetEvent, ManualResetEvent共同的祖先,他们包装了用于同步的内核对象,也就是说是 这些内核对象的托管版本。Mut ex:类似于一个接力棒,拿到接力棒的线程才可以开始跑,当然接力棒一次 只属于一个线程(Thread Affinity),如果这个线程不释放接力棒(Mutex.ReleaseMutex

42、),那么没办法,其他所有需要接力棒运行的线程都知道能 等着看热闹Semaphore:类似于一个小桶,里面装了几个小球,凡是拿到小球就可以跑,比如 指定小桶里最初有四个小球,那么开始的四个线程就可以直接拿着自己的小球开 跑,但是第五个线程一看,小球被拿光了,就只好乖乖的等着有谁放一个小球到 小桶里(Semophore.Release),他才能跑,但是这里的游戏规则比较特殊,我们 可 以随意向小桶里放入小球,也就是说我可以拿走一个小球,放回去俩,甚至一个都 不拿,放回去5个,这样就有五个线程可以拿着这些小球运行了.我们可以规定 小桶里有开始有几个小球(构造函数的第一个参数),也可以规定最多不能超过

43、多 少小球(构造函数的第二个参数)什么是用双锁实现Single ton,为什么要这样做,双锁检验是不安全 的吗?使用双锁检验技巧来实现单件,来自于Java社区public static MySingleton Instancegetif(_instance!=null)lock(_instance)if(s_value=null)_instance= new MySingleton();这样做其实是为了提高效率,比起public static MySingleton Instancegetlock(_ins tance)if(s_value=null)_ins tance二 new MySin

44、gle ton();前一种方法在instance创建的时候不需要用lock同步,从而增进了效率在java中这种技巧被证明是不安全的详细见htt p:/www.cs.umd.edu/pugh/java/memoryModel/但是在.Net下,这样的技巧是成立的,因为.Net使用了改进的内存模型 并且在.Net下,我们可以使用LazyInit来实现单件 private static readonly _instance二new MySingleton() public static MySingleton Instancegetreturn _instance当第一此使用_instance时,C

45、LR会生成这个对象,以后再访问这个字段,将会直 接返回互斥对象(Mutex),信号量(Semaphore), 事件(Event)对象与lock语句的比较首先这里所谓的事件对象不是System.Event,而是一种用于同步的内核机制互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,线程必须要在 用户模式和内核模式间切换,所以一般效率很低,但利用互斥对象和事件对象这 样的内核对象,可以在多个进程中的各个线程间进行同步。lock或者Moni ter是.net用一个特殊结构实现的,不涉及模式切换,也就是说工 作在用户方式下,同步速度较快,但是不能跨进程同步什么时候需要锁定?刚刚接触锁定的程序员

46、往往觉得这个世界非常的危险,每个静态变量似乎都有可 能产生竞争首先锁定是解决竞争条件的,也就是多个线程同时访问某个资源,造成意想不到 的结果,比如,最简单的情况,一个计数器,如果两个线程同时加一,后果就是损失 了一个计数,但是频繁的锁定又可能带来性能上的消耗,还有最可怕的情况,死锁到底什么情况下我们需要使用锁,什么情况下不用呢?只有共享资源才需要锁定首先,只有可以被多线程访问的共享资源才需要考虑锁定,比如静态变量,再比如 某些缓存中的值,属于线程内部的变量不需要锁定把锁定交给数据库数据库除了存储数据之外,还有一个重要的用途就是同步,数据库本身用了一套 复杂的机制来保证数据的可靠和一致性,这就为

47、我们节省了很多的精力保证了 数据源头上的同步,我们多数的精力就可以集中在缓存等其他一些资源的同步访 问上了了解你的程序是怎么运行的实 际上在web开发中大多数逻辑都是在单个线程中展开的,无论还是 php, 个请求都会在一个单独的线程中处理,其中的大部分变量都是属于这 个 线程的,根本没有必要考虑锁定,当然对于中的application对象中的数 据,我们就要小心一些了WinForm中凡是使用BeginInvoke和Invoke调用的方法也都不需要考虑同步,因 为这用这两个方法调用的方法会在UI线程中执行,因此实际是同步的,所以如果 调用的方法中存在某些静态变量,不需要考虑锁定业务逻辑对事务和线

48、程安全的要求这条是最根本的东西,开发完全线程安全的程序是件很费时费力的事情,在电子 商务等涉及金融系统的案例中,许多逻辑都必须严格的线程安全,所以我们不得 不牺牲一些性能,和很多的开发时间来做这方面的工作,而一般的应用中,许多 情况下虽然程序有竞争的危险,我们还是可以不使用锁定,比如有的时候计数器 少一多一,对结果无伤大雅的情况下,我们就可以不用去管他计算一下冲突的可能性我以前曾经谈到过,架构不要过设计,其实在这里也一样,假如你的全局缓存里 的某个值每天只有几百或者几千个访问,并且访问时间很短,并且分布均匀(实际 上这是大多数的情况),那么冲突的可能性就非常的少,也许每500天才会出现 一次或

49、者更长,从7*24小时安全服务的角度来看,也完全符合要求,那么你还会 为这样万分之一的可能性花80%的精力去设计吗?请多使用lock,少用Mutex如果你一定要使用锁定,请尽量不要使用内核模块的锁定机制,比如.net的Mutex,Semaphore,AutoResetEvent, ManuResetEvent,使用这样的机制涉及到了 系统在用户模式和内核模式间的切换,所以性能差很多,但是他们的优点是可以 跨进程同步线程,所以应 该清楚的了解到他们的不同和适用范围Web 和 IIS应用程序池, WebApplication,和线程池之间有什么关系一个应用程序池是一个独立的进程,拥有一个线程池,应

50、用程序池中可以有多个 WebApplication,每个运行在一个单独的 AppDomain 中,这些 WebApplication 公 用一个线程池不同的AppDomain保证了每个WebApplication的静态变量不会互相干扰,不同的 应用程序池保证了一个网站瘫痪,其他不同进程中的站点还能正常运行下图说明了他们的关系:i: :.: :.: :.: :.: :.: :.: :.: :.:: :.: :.: :.: :.: :.: :.: :.: :.:: :.: :.: :.: :.: :.: :.: :.: :.: i;:.: :.: :.: :.: :.: :.: :.: :.: :

51、.: :.: :.:;:.: :.: :.: :.: :.: :.: :.: :.:::.: :;:::;:::;:::;::富富富富富富:个进程: :.: :.: :.: :;:::;:::;:::;:加滋滋-omain:憑総g総滋総:総総:総総簿総総滋総:総鑼:総総滋:総:君: : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :-. . .I、 -flt 1. I/tnZZZQ?:広懣逐: :?:?:?:?: 应用程序池omainv-v.-:-H宀:X X X:11:.:.B:1:.:.:-:.:-:.:-:.:.:.:.:.:.:.:.:.-.Web页面怎么调用异步WebService把Page的Async属性设置为true,就可以调用异步的方法,但是这样调用的效果 可能并不如我们的相像,请参考Web中使用多线程来增强用户体验推荐文章A low-level Look at the ASP.NET Architecture参考资料 Windows核心编程这本书里对内核对象的描述比较详尽

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!