.Net线程问题解答(转)

原文:http://blog.csdn.net/yizhu2000/archive/2008/01/03/2022264.aspx

把遇到.Net线程的一些问题解集中起来和大家分享,也希望大家能一起,讨论


·       样创建一个线

·       BeginXXXEndXXX的方法是做什用的

·       和多线程有什么关联

WinForm线程篇

·       我的多线WinForm程序老是抛出InvalidOperationException ,怎解决?

·       InvokeBeginInvoke干什用的,内部是怎么实现

·       线程都有消息?

·       Winform不允线程修改UI线程控件的

·       有没有什么办法可以WinForm线程的开发

线程池

·       线程池的作用是什?

·       所有程使用一个共享的线程池,程使用独立的线程池?

·       不要手动线程池置最大?

·       .Net线程池有什不足?

·       CLR样实现lock(obj)?

·       WaitHandle是什,他和他的派生使用

·       是用双锁实现Singleton,这样,有人锁检验是不安全的

·       互斥象(Mutex)、事件(Event象与lock句的比

么时候需要

·       只有共享源才需要

·       定交数据

·       了解你的程序是怎运行的

·       业务逻辑对线程安全的要求

·       算一下冲突的可能性

·       多使用lock,少用Mutex

WebIIS

·       用程序池,WebApplication,线程池之有什么关

·       Web面怎么调用异WebService

 样创建一个线

我只简单常用的方法,详细可参.Net线总结()

一)使用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 Diameter of {0} is {1}"Diameter,Diameter*Math.PI);
}

二)使用Delegate.BeginInvoke

delegate double CalculateMethod(double Diameter); //申明一个委托,表明需要在子线程上行的方法的函数

CalculateMethod calcMethod = 
new CalculateMethod(Calculate);//把委托和具体的方法关联起来
//
处开始异步执,并且可以出一个回函数(如果不需要行什操作也可以不使用回)
calcMethod.BeginInvoke(5new AsyncCallback(TaskFinished), null);

//线用的函数,出直径作参数,算周
public static double Calculate(double Diameter){
return Diameter * Math.PI;
}

//线程完成之后回的函数
public static void TaskFinished(IAsyncResult result){
result=calcMethod.EndInvoke(result);
Console.WriteLine(result);
}

三)使用ThreadPool.QueueworkItem

WaitCallback 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 Diameter)
{
return Diameter * Math.PI;
}

常看到名BeginXXXEndXXX的方法,他是做什

.net的一个异方法名称
.Net
设计步编设计了一个异步编程模型(APM),个模型不是使用.NET开发使用,.Net内部也繁用到,比如所有的 Stream就有BeginReadEndReadSocket,WebRequet,SqlCommand都运用到了个模式,一般来 BegionXXX候,一般会启一个异步过程去行一个操作,EndEnvoke可以接收个异操作的返回,当然如果异操作在 EndEnvoke用的没有行完成,EndInvoke会一直等待异操作完成或者超

.Net的异步编程模型(APM)一般包含BeginXXXEndXXXIAsyncResult三个元素,BeginXXX方法都要返回一个IAsyncResult,而EndXXX都需要接收一个IAsyncResult参数,他的函数名模式如下

IAsyncResult BeginXXX(...);

<返回> EndXXX(IAsyncResult ar)

BeginXXXEndXXX中的XXX,一般都对应一个同的方法,比如FileStreamRead方法是一个同方法,相 BeginRead(),EndRead()就是他的异版本,HttpRequestGetResponse来同接收一个响,也提供了 BeginGetResponseEndGetResponse个异版本,而IAsynResult是二者系的纽带,只有把BeginXXX所返回的IAsyncResult传给对应EndXXXEndXXX才知道需要去接收哪个BeginXXX起的异操作的返回

个模式在实际使用,然原上我可以随时调EndInvoke得返回,并且可以同多个线,但是大多数情况下当我不需要很多线程的候使用回是更好的选择,这种情况下三个元素中的IAsynResult得多余,一不需要用其中的线程完结标志来判断线程是否成功完成(线应该完成了),二不需要他来传递数据,数据可以写在任何量里,并且回调时应该填充,所以可以看到微在新的. Net Framework中已事件的支持,这总模型下,典型的回程序应该这样

a.DoWork+=new SomeEventHandler(Caculate);
a.CallBack+=new SomeEventHandler(callback);
a.Run();

(注:我上面的是普遍的用法,然而BeginXXXEndXXX仅仅是一模式,而对这个模式的实现完全取决于使用他的开发,具体实现候你可以使用另外一个线程来实现,也可能使用硬件的支持来实现,甚至可能根本和异没有系(尽管几乎没有人会这样做)-----比如直接在 Beginxxx里直接出一个"Helloworld",如果是这种极端的情况,那上面的一切都是废话,所以上面的探并不及内部实现,只是告大家微的模式,和框架中对这个模式的实现

和多线程有什么关联

有一句话总结的很好:多线程是实现的一手段和工具

通常把多线程和异等同起来,实际是一种误解,在实际实现候,异种实现方法,我可以用程来做异,或者使用程,或者硬件的一些特性,比如在实现IO,可以有下面两个方案:

1)可以通初始化一个子线程,然后在子线程里IO,而线利往下行,当子线行完就回

2)也可以根本不使用新线程,而使用硬件的支持(多硬件都有自己的理器),来实现完全的异是我只需要将IO求告知硬件驱动程序,然后迅速返回,然后等着硬件IO通知我就可以了

实际DotNet Framework里面就有这样的例子,当我使用文件流的候,如果制定文件流属性使用BeginRead,就是用一个子线程来用同Read方法,而如果指定其操作就使用了需要硬件和操作系支持的所IOCP的机制

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线程)上访问控件的属性就会抛出错误,解决方法就是利用控件提供的 InvokeBeginInvoke用封送回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(result);
//
算完成需要在一个文本框里
}
public void CalcFinished(double result){
    this.TextBox1.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(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.Result);
    }
    else{
        this.TextBox1.Text=result.ToString();
    }
}

上面的做法用到了Control的一个属性InvokeRequired个属性是可以在其他线程里访问的),个属性表明用是否来自另非UI线,如果是,使用BeginInvoke个函数,就直接,省去线程封送的

InvokeBeginInvoke干什用的,内部是怎么实现?

两个方法主要是让给出的方法在控件建的线程上

Invoke使用了Win32APISendMessage,

UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);

BeginInvoke使用了Win32APIPostMessage

UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);

两个方法向UI线程的消息列中放入一个消息,UI线个消息,就会在自己的上下文中入的方法,话说凡是使用BeginInvokeInvoke用的线程都是在UI线程中行的,所以如果些方法里及一些静态变,不用考问题

线程都有消息?

不是,只有建了窗体象的线程才会有消息(下面<Windows 核心>一段的描述)

当一个线程第一次被建立,系假定线程不会被用于任何与用的任这样可以减少线统资源的要求。但是,一旦线用一个与形用界面有的函数(例如检查它的消息列或建立一个窗口),系就会为该线程分配一些另外的源,以便它能够执行与用界面有的任。特是,系分配一个T H R E A D I N F O构,并将个数据构与线系起来。

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


Winform不允线程修改UI线程控件的

vs2003,使用子线ui线建的控件的属性是不会有问题,但是编译候会出警告,但是vs2005及以上版本就会有这样问题,下面是msdn上的描述

"当您在 Visual Studio 调试器中运行代码时,如果您从一个线访问某个 UI 元素,而该线程不是 UI 元素所在的线程,会引 InvalidOperationException调试器引发该异常以警告您存在危程操作。UI 元素不是线程安全的,所以只建它线程上访问"

从上面可以看出,个异常实际debugger耍的花招,也就是,如果你直接运行程序的exe文件,或者利用运行而不调试(Ctrl+F5)运行你的程序,是不会抛出这样的异常的.大概ms发现v2003的警告广大开发者不起作用,所以用了一个比狠一点的方法.

过问题依然存在:既然这样设计的原因主要是因控件的线程安全,DotNet framework中非线程安全的千千万万,偏偏跨线程修改Control的属性会有这样严格的限制策略呢?

问题回答不好,希望博友予以

有没有什么办法可以WinForm线程的开发

使用backgroundworker,使用建可以避免回调时InvokeBeginInvoke,并且提供了多丰富的方法和事件

.Net线总结()-BackgroundWorker,我在里不再赘诉

线

线程池的作用是什

作用是减小线建和销毁开销

线及用模式和内核模式的切,内存分配,dll通知等一系列程,线销毁步骤也是开销很大的,所以如果用程序使用了完一个线程,我能把线暂时存放起来,以下次使用,就可以减小开销

所有程使用一个共享的线程池,程使用独立的线程池?

程都有一个线程池,一个Process中只能有一个例,它在各个用程序域(AppDomain)是共享的,.Net2.0 中默认线程池的大小工作线25,IO线1000,有一个比普遍的解是线程池中会有1000线程等着你去取,不然, ThreadPool仅仅保留相当少的线程,保留的线程可以用SetMinThread个方法来置,当程序的某个地方需要建一个线程来完成工作线程池中又没有空闲线,线程池就会负责创线,并且在用完,不会立刻销毁,而是把他放在池子里,预备下次使用,如果线程超一定没有被使用,线程池将会回收线程,所以线程池里存在的线程数实际是个动态

不要手动线程池置最大?

当我首次看到线程池的,袋里的第一个念就是定一个最大,然而当我们查ThreadPoolSetMaxThreads文档往往会看到一条警告:不要手更改线程池的大小,?

FileStream的异步读,步发送接受Web,甚至使用delegatebeginInvoke都会默认调 ThreadPool,也就是你的代可能使用到线程池,框架内部也可能使用到,更改的后果影响就非常大,iis,一个用程序池中的所有 WebApplication会共享一个线程池,最大定会来很多意想不到的麻

线程池的线何要分?

线程池有一个方法可以看到线程池中可用的线程数量:GetAvaliableThread(out workerThreadCount,out iocompletedThreadCount),于我来,第一次看到个函数的参数十分困惑,我期望个函数直接返回一个整形,表明剩多少线,个函数居然一次返回了两个.

原来线程池里的线程按照公用被分成了两大:工作线程和IO线,或者IO完成线,前者用于行普通的操作,后者用于异IO,比如文件和网,注意,并不明两种线程本身有差,线程就是线,是一种执,从本上来都是一,线程池这样,例来,就好像某施工工地1000铁锹,定其中25后勤部,其他都施工部,施工部需要大量使用铁锹来挖地基(例子土了点,过说问题还是有效的),后勤部铁锹也就是铲铲,铲铲垃圾,工人傅修修临时住房,所以用量不大,然两个部铁锹本身没有区,但是这样的划分就管理两个部铁锹提供了方便

线程池中两种线程分在什情况下被使用,二者工作原理有什不同?

下面个例子直接明了二者的区,用一个流出一个很大的文件(大一点操作的时间长,便于),然后用另一个出流把所出的文件的一部分写到磁

用两方法出流,

建了一个异的流(注意构造函数最后那个true)

FileStream outputfs=new FileStream(writepath, FileMode.Create, FileAccess.Write, FileShare.None,256,true);

建了一个同的流

FileStream outputfs = File.OpenWrite(writepath);

 然后在写文件期间查线程池的状况

string readpath = "e:""RHEL4-U4-i386-AS-disc1.iso";
string writepath = "e:""kakakak.iso";
byte[] buffer = new byte[90000000];

//FileStream outputfs=new FileStream(writepath, FileMode.Create, FileAccess.Write, FileShare.None,256,true);
//Console.WriteLine("
");
//
建了一个同的流

FileStream outputfs = File.OpenWrite(writepath);
Console.WriteLine(
"");

 
//然后在写文件期间查线程池的状况

ShowThreadDetail(
"初始状");

FileStream fs = File.OpenRead(readpath);

fs.BeginRead(buffer, 
090000000delegate(IAsyncResult o)
{

    outputfs.BeginWrite(buffer, 
0, buffer.Length,

    
delegate(IAsyncResult o1)
    
{

        Thread.Sleep(
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(
out Worker, out IO);
    Console.WriteLine(
"Worker: {0}; IO: {1}", Worker, IO);
}





Worker: 500; IO: 1000

Worker: 500; IO: 999



Worker: 500; IO: 1000

Worker: 499; IO: 1000

 

两个构造函数建的流都可以使用BeginWrite来异写数据,但是二者行不同,当使用同的流行异,出我可以看到,他使用的是工作线,而非IO线,而异流使用了IO线程而非工作线

当没有制定异属性的,.Net实现IO是用一个子线fs的同Write方法来实现,这时这个子线程会一直阻塞直到用完.个子线程其就是线程池的一个工作线,所以我可以看到,流的异写回出的工作线程数少了一,而使用异,行异,采用了 IOCP方法,简单说,就是当BeginWrite,把信息传给硬件驱动程序,然后立即往下(注意里没有外的线),而当硬件准, 就会通知线程池,使用一个IO线程来

.Net线程池有什

没有提供方法控制加入线程池的线:一旦加入线程池,没有法挂起,线,唯一可以做的就是等他自己

不能为线

一个Process中只能有一个例,它在各个AppDomain是共享的。ThreadPool只提供了静方法,不自己添加去的WorkItem使用Pool,而且.net framework中那些BeginXXXEndXXX的方法都会使用此Pool

所支持的Callback不能有返回WaitCallback只能一个object型的参数,没有任何返回

不适合用在行某任合。我常常需要做一个Service来提供不断的服(除非服down掉),但是使用ThreadPool并不合适。

定与同

CLR样实现lock(obj)?

从原理上,lockSyncronized Attribute都是用Moniter.Enter实现,比如如下代

object lockobj=new object();
lock(obj){  
//do things 
}

 
编译时,会被编译为类

try{
  Moniter.Enter(obj)
{
   
//do things
  }
}
catch{ }
finally{
  Moniter.Exit(obj);
}



[MethodImpl(MethodImplOptions.Synchronized)]标记为的方法会在编译时lock(this)句所环绕
所以我简单Moniter.Enter实现

(注:DotNet并非使用Win32APICriticalSection实现Moniter.Enter,不托管象提供了一个似的构叫做Syncblk

部都有一个指,个指指向的,包含了象的定信息,当第一次使用Moniter.Enter(obj),obj构就会被初,第二次Moniter.Enter,检验这object,如果没有被,则调用会阻

WaitHandle是什,他和他的派生使

  WaitHandleMutexSemaphoreEventWaitHandlerAutoResetEventManualResetEvent共同的祖先,他包装了用于同的内核象,也就是些内核象的托管版本。

  Mutex:似于一个接力棒,拿到接力棒的线程才可以始跑,当然接力棒一次只属于一个线(Thread Affinity),如果线程不放接力棒(Mutex.ReleaseMutex),,其他所有需要接力棒运行的线程都知道能等着看热闹

Semaphore:似于一个小桶,里面装了几个小球,凡是拿到小球就可以跑,比如指定小桶里最初有四个小球,么开始的四个线程就可以直接拿着自己的小球,但是第五个线程一看,小球被拿光了,就只好乖乖的等着有放一个小球到小桶里(Semophore.Release),他才能跑,但是里的游戏规则特殊,可以随意向小桶里放入小球,也就是我可以拿走一个小球,放回去,甚至一个都不拿,放回去5,这样就有五个线程可以拿着些小球运行了.可以定小桶里有始有几个小球(构造函数的第一个参数),也可以定最多不能超多少小球(构造函数的第二个参数)

是用双锁实现Singleton,这样,锁检验是不安全的?

使用双锁检验技巧来实现单,来自于Java社区

public static MySingleton Instance{
get{
    
if(_instance!=null)}{
        
lock(_instance){
            
if(s_value==null){
                _instance= 
new MySingleton();
            }
        }
    }
}
}

这样做其了提高效率,比起

public static MySingleton Instance{

get{

lock(_instance){

if(s_value==null){

_instance= new MySingleton();

}

}

前一方法在instance建的候不需要用lock,从而增了效率

java这种技巧被明是不安全的详细见WWW.CS.UMD.EDU/~PUGH/JAVA/MEMORYMODEL

但是在.Net,这样的技巧是成立的,.Net使用了改的内存模型

并且在.Net,可以使用LazyInit实现单

private static readonly _instance=new MySingleton()

public static MySingleton Instance{

get{return _instance}

}

当第一此使用_instance,CLR会生成,以后再访问这个字段,将会直接返回

互斥象(Mutex,信号量(Semaphore),事件(Event象与lock句的比

首先里所的事件象不是System.Event,而是一用于同的内核机制

互斥象和事件象属于内核象,利用内核线程同线程必要在用模式和内核模式,所以一般效率很低,但利用互斥象和事件这样的内核象,可以在多个中的各个线间进行同

lock或者Moniter.net用一个特殊实现,及模式切,也就是工作在用方式下,同速度,但是不能跨程同

么时候需要?

刚刚接触定的程序往往个世界非常的危,个静态变量似乎都有可能

首先定是解决争条件的,也就是多个线程同时访问某个,造成意想不到的,比如,简单的情况,一个数器,如果两个线程同加一,后果就是失了一个,但是繁的定又可能来性能上的消耗,有最可怕的情况,

到底什情况下我需要使用,情况下不用呢?

只有共享源才需要
首先,只有可以被多线访问的共享源才需要考虑锁,比如静态变,再比如某些存中的,属于线程内部的量不需要

定交数据
数据除了存数据之外,有一个重要的用途就是同,数据本身用了一套复杂的机制来保数据的可靠和一致性,们节省了很多的精力.了数据源上的同,多数的精力就可以集中在存等其他一些源的同步访问上了

了解你的程序是怎运行
上在web开发中大多数逻辑都是在线程中展,asp.netphp,一个求都会在一个独的线程中,其中的大部分量都是属于线程的,根本没有必要考虑锁,当然asp.net中的application象中的数据,就要小心一些了

WinForm中凡是使用BeginInvokeInvoke用的方法也都不需要考,为这两个方法用的方法会在UI线程中,因此实际是同,所以如果用的方法中存在某些静态变,不需要考虑锁

业务逻辑对线程安全的要
条是最根本的西,开发完全线程安全的程序是件很费时费力的事情,子商及金融系的案例中,逻辑都必须严格的线程安全,所以我不得不牲一些性能,和很多的开发时间来做方面的工作,而一般的用中,多情况下然程序有争的危,们还是可以不使用,比如有的数器少一多,对结果无大雅的情况下,就可以不用去管他

算一下冲突的可能
以前曾经谈,架构不要过设计,里也一,假如你的全局存里的某个值每天只有几百或者几千个访问,并且访问时间很短,并且分布均匀(实际是大多数的情况),冲突的可能性就非常的少,许每500天才会出一次或者更,7*24安全服的角度来看,也完全符合要求,这样万分之一的可能性花80%的精力去设计吗?

多使用lock,少用Mutex
果你一定要使用,尽量不要使用内核模定机制,比如.netMutex,Semaphore,AutoResetEvent, ManuResetEvent,使用这样的机制及到了系在用模式和内核模式的切,所以性能差很多,但是他点是可以跨程同步线,所以清楚的了解到他的不同和适用范

WebIIS

用程序池,WebApplication,线程池之有什么关

一个用程序池是一个独立的,有一个线程池,用程序池中可以有多个WebApplication,个运行在一个独的AppDomain,WebApplication公用一个线程池

不同的AppDomainWebApplication的静态变量不会互相干,不同的用程序池保了一个网站瘫痪,其他不同程中的站点能正常运行

 图说明了他

Web面怎么调用异WebService

PageAsync属性true,就可以用异的方法,但是这样调用的效果可能并不如我的相像,Web中使用多线程来增

推荐文章

http://alang79.blogdriver.com/alang79/456761.html

A low-level Look at the ASP.NET Architecture

参考

<Windows 核心>内核象的描述比较详

原文地址:https://www.cnblogs.com/cuihongyu3503319/p/1025412.html