您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 经营企划 > Net线程的一些问题和误解
目录基础篇怎样创建一个线程名为BeginXXX和EndXXX的方法是做什么用的异步和多线程有什么关联WinForm多线程编程篇我的多线程WinForm程序老是抛出InvalidOperationException,怎么解决?Invoke,BeginInvoke干什么用的,内部是怎么实现的每个线程都有消息队列吗?为什么Winform不允许跨线程修改UI线程控件的值有没有什么办法可以简化WinForm多线程的开发线程池线程池的作用是什么?所有进程使用一个共享的线程池,还是每个进程使用独立的线程池?为什么不要手动线程池设置最大值?.Net线程池有什么不足?同步CLR怎样实现lock(obj)锁定?WaitHandle是什么,他和他的派生类怎么使用什么是用双锁实现Singleton,为什么要这样做,为什么有人说双锁检验是不安全的互斥对象(Mutex)、事件(Event)对象与lock语句的比较什么时候需要锁定只有共享资源才需要锁定把锁定交给数据库了解你的程序是怎么运行的业务逻辑对事务和线程安全的要求计算一下冲突的可能性请多使用lock,少用MutexWeb和IIS应用程序池,WebApplication,和线程池之间有什么关系Web页面怎么调用异步WebService基础篇怎样创建一个线程我只简单列举几种常用的方法,详细可参考.Net多线程总结(一)一)使用Thread类ThreadStartthreadStart=newThreadStart(Calculate);//通过ThreadStart委托告诉子线程讲执行什么方法,这里执行一个计算圆周长的方法Threadthread=newThread(threadStart);thread.Start();//启动新线程publicvoidCalculate(){doubleDiameter=0.5;Console.Write(TheperimeterOfCirclewithaDiameterof{0}is{1}Diameter,Diameter*Math.PI);}二)使用Delegate.BeginInvokedelegatedoubleCalculateMethod(doubleDiameter);//申明一个委托,表明需要在子线程上执行的方法的函数签名CalculateMethodcalcMethod=newCalculateMethod(Calculate);//把委托和具体的方法关联起来//此处开始异步执行,并且可以给出一个回调函数(如果不需要执行什么后续操作也可以不使用回调)calcMethod.BeginInvoke(5,newAsyncCallback(TaskFinished),null);//线程调用的函数,给出直径作为参数,计算周长publicstaticdoubleCalculate(doubleDiameter){returnDiameter*Math.PI;}//线程完成之后回调的函数publicstaticvoidTaskFinished(IAsyncResultresult){result=calcMethod.EndInvoke(result);Console.WriteLine(result);}三)使用ThreadPool.QueueworkItemWaitCallbackw=newWaitCallback(Calculate);//下面启动四个线程,计算四个直径下的圆周长ThreadPool.QueueUserWorkItem(w,1.0);ThreadPool.QueueUserWorkItem(w,2.0);ThreadPool.QueueUserWorkItem(w,3.0);ThreadPool.QueueUserWorkItem(w,4.0);publicstaticvoidCalculate(doubleDiameter){returnDiameter*Math.PI;}经常看到名为BeginXXX和EndXXX的方法,他们是做什么用的这是.net的一个异步方法名称规范.Net在设计的时候为异步编程设计了一个异步编程模型(APM),这个模型不仅是使用.NET的开发人员使用,.Net内部也频繁用到,比如所有的Stream就有BeginRead,EndRead,Socket,WebRequet,SqlCommand都运用到了这个模式,一般来讲,调用BegionXXX的时候,一般会启动一个异步过程去执行一个操作,EndEnvoke可以接收这个异步操作的返回,当然如果异步操作在EndEnvoke调用的时候还没有执行完成,EndInvoke会一直等待异步操作完成或者超时.Net的异步编程模型(APM)一般包含BeginXXX,EndXXX,IAsyncResult这三个元素,BeginXXX方法都要返回一个IAsyncResult,而EndXXX都需要接收一个IAsyncResult作为参数,他们的函数签名模式如下IAsyncResultBeginXXX(...);返回类型EndXXX(IAsyncResultar);BeginXXX和EndXXX中的XXX,一般都对应一个同步的方法,比如FileStream的Read方法是一个同步方法,相应的BeginRead(),EndRead()就是他的异步版本,HttpRequest有GetResponse来同步接收一个响应,也提供了BeginGetResponse和EndGetResponse这个异步版本,而IAsynResult是二者联系的纽带,只有把BeginXXX所返回的IAsyncResult传给对应的EndXXX,EndXXX才知道需要去接收哪个BeginXXX发起的异步操作的返回值。这个模式在实际使用时稍显繁琐,虽然原则上我们可以随时调用EndInvoke来获得返回值,并且可以同步多个线程,但是大多数情况下当我们不需要同步很多线程的时候使用回调是更好的选择,在这种情况下三个元素中的IAsynResult就显得多余,我们一不需要用其中的线程完结标志来判断线程是否成功完成(回调的时候线程应该已经完成了),二不需要他来传递数据,因为数据可以写在任何变量里,并且回调时应该已经填充,所以可以看到微软在新的.NetFramework中已经加强了对回调事件的支持,这总模型下,典型的回调程序应该这样写a.DoWork+=newSomeEventHandler(Caculate);a.CallBack+=newSomeEventHandler(callback);a.Run();(注:我上面讲的是普遍的用法,然而BeginXXX,EndXXX仅仅是一种模式,而对这个模式的实现完全取决于使用他的开发人员,具体实现的时候你可以使用另外一个线程来实现异步,也可能使用硬件的支持来实现异步,甚至可能根本和异步没有关系(尽管几乎没有人会这样做)-----比如直接在Beginxxx里直接输出一个Helloworld,如果是这种极端的情况,那么上面说的一切都是废话,所以上面的探讨并不涉及内部实现,只是告诉大家微软的模式,和框架中对这个模式的经典实现)异步和多线程有什么关联有一句话总结的很好:多线程是实现异步的一种手段和工具我们通常把多线程和异步等同起来,实际是一种误解,在实际实现的时候,异步有许多种实现方法,我们可以用进程来做异步,或者使用纤程,或者硬件的一些特性,比如在实现异步IO的时候,可以有下面两个方案:1)可以通过初始化一个子线程,然后在子线程里进行IO,而让主线程顺利往下执行,当子线程执行完毕就回调2)也可以根本不使用新线程,而使用硬件的支持(现在许多硬件都有自己的处理器),来实现完全的异步,这是我们只需要将IO请求告知硬件驱动程序,然后迅速返回,然后等着硬件IO就绪通知我们就可以了实际上DotNetFramework里面就有这样的例子,当我们使用文件流的时候,如果制定文件流属性为同步,则使用BeginRead进行读取时,就是用一个子线程来调用同步的Read方法,而如果指定其为异步,则同样操作时就使用了需要硬件和操作系统支持的所谓IOCP的机制WinForm多线程编程篇我的多线程WinForm程序老是抛出InvalidOperationException,怎么解决?在WinForm中使用多线程时,常常遇到一个问题,当在子线程(非UI线程)中修改一个控件的值:比如修改进度条进度,时会抛出如下错误Cross-threadoperationnotvalid:Control'XXX'accessedfromathreadotherthanthethreaditwascreatedon.在VS2005或者更高版本中,只要不是在控件的创建线程(一般就是指UI主线程)上访问控件的属性就会抛出这个错误,解决方法就是利用控件提供的Invoke和BeginInvoke把调用封送回UI线程,也就是让控件属性修改在UI线程上执行,下面列出会报错的代码和他的修改版本ThreadStartthreadStart=newThreadStart(Calculate);//通过ThreadStart委托告诉子线程讲执行什么方法Threadthread=newThread(threadStart);thread.Start();publicvoidCalculate(){doubleDiameter=0.5;doubleresult=Diameter*Math.PI;CalcFinished(result);//计算完成需要在一个文本框里显示}publicvoidCalcFinished(doubleresult){this.TextBox1.Text=result.ToString();//会抛出错误}上面加粗的地方在debug的时候会报错,最直接的修改方法是修改Calculate这个方法如下delegatevoidchangeText(doubleresult);publicvoidCalculate(){doubleDiameter=0.5;doubleresult=Diameter*Math.PI;this.BeginInvoke(newchangeText(CalcFinished),t.Result);//计算完成需要在一个文本框里显示}这样就ok了,但是最漂亮的方法是不去修改Calculate,而去修改CalcFinished这个方法,因为程序里调用这个方法的地方可能很多,由于加了是否需要封送的判断,这样修改还能提高非跨线程调用时的性能delegatevoidchangeText(doubleresult);publicvoidCalcFinished(doubleresult){if(this.InvokeRequired){this.BeginInvoke(newchangeText(CalcFinished),t.Result);}else{this.TextBox1.Text=result.ToString();}}上面的做法用到了Control的一个属性InvokeRequired(这个属性是可以在其他线程里访问的),这个属性表明调用是否来自另非UI线程,如果是,则使用BeginInvoke来调用这个函数,否则就直接调用,省去线程封送的过程Invoke,BeginInvoke干什么用的,内部是怎么实现的?这两个方法主要是让给出的方法在控件创建的线程上执行Invoke使用了Win32API的SendMessage,UnsafeNativeMethods.PostMessage(newHandleRef(this,this.Handle),threadCallbackMessage,IntPtr.Zero,IntPtr.Zero);BeginInvoke使用了Win32API的PostMessageUnsafeNativeMethods.PostMessage
本文标题:Net线程的一些问题和误解
链接地址:https://www.777doc.com/doc-3401868 .html