Windows系列操作系统是建立在保护模式之上的32位/64位多任务操作系统,其特点是:时分抢先式多任务操作系统。我们来详细探讨一下其中的定义
Windows操作系统是如何来启动一个应用程序(.exe文件):
操作系统分配一个进程,并向CPU下达一系列指令,包括创建该进程的虚拟内存映射表(关于虚拟内存,请参阅操作系统原理相关书籍,32位保护模式这一概念),分配虚拟内存,设定进程描述符等等,最终将进程封闭到一个独立的虚拟内存地址空间中,返回进程句柄; 操作系统为该进程创建一个线程,并启动它; 该线程从应用程序的Main方法开始执行。 也即是说,所有的Windows进程,都缺省拥有一个线程,这个线程称为主线程;主线程可以根据代码要求创建其它线程,称为辅助线程。
操作系统是如何在CPU上调度多个线程:
Windows操作系统采用了“时分”的概念来调度多个线程,即每个线程占用CPU一段时间后,就会被强行将执行权交给下一个线程。线程轮流来使用CPU。使用CPU的线程,正常执行程序,时间到达时,操作系统执行线程切换操作:将CPU寄存器的内容全部转移到内存中,将另一个线程保存的寄存器状态从内存恢复到寄存器中,由于寄存器中保存了某个线程正在运行哪一行代码的指针,所以切换后另一个线程可以继续上一次运行状态继续运行。这种切换线程的方式称为:现场保存和现场恢复。
控制线程的Thread类和方法
Thread.Start()方法:
开始线程
Thread.IsAlive 属性:
如果此线程已启动并且尚未正常终止或中止,则为 true;否则为 false。
Suspend():该方法并不终止未完成的线程,它仅仅挂起线程,以后还可恢复;
Resume():恢复被Suspend()方法挂起的线程的执行。
Thread.ThreadState 属性
Aborted:线程已停止;
AbortRequested:线程的Thread.Abort()方法已被调用,但是线程还未停止;
Background:线程在后台执行,与属性Thread.IsBackground有关;不妨碍程序的终止
Running:线程正在正常运行;
Stopped:线程已经被停止;
StopRequested:线程正在被要求停止;
Suspended:线程已经被挂起(此状态下,可以通过调用Resume()方法重新运行);
SuspendRequested:线程正在要求被挂起,但是未来得及响应;
Unstarted:未调用Thread.Start()开始线程的运行;
WaitSleepJoin:线程因为调用了Wait(),Sleep()或Join()等方法处于封锁状态;
线程的优先级
由高到低分别是Highest,AboveNormal,Normal,BelowNormal,Lowest;系统默认为ThreadPriority.Normal
指定优先级的代码:myThread.Priority=ThreadPriority.Lowest;
多线程的优劣
优点:可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。
缺点:线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
多线程需要协调和管理,所以需要CPU时间跟踪线程;
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
线程太多会导致控制太复杂,最终可能造成很多Bug;
如何的安全的退出线程:
按照要求,无论使用何种编程语言,线程都必须自然退出,而不应该被迫退出。所谓自然退出。在.net Framework中没有提供任何可以杀死线程的方法,所以我们只能等待线程执行完毕后自然死亡。
I/强制杀死线程(非安全)....Abord方法:停止线程的执行
在一个线程中停止另一个线程的执行,此时被终止的线程立即抛出一个ThreadAbortException异常,通过抛出异常跳出线程代码执行。通过try…catch(ThreadAbortException)…段包围线程方法内的代码,可以在线程被中止后得到一个通知。
I/设置一段时间等待线程之后再调用Abord方法(安全)...Join方法:等待该线程终止。
在一个父线程(如主线程)调用Join,等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。(Waits for this thread to die.)
if (SubThread.ThreadState == ThreadState.Running) { // 先尝试等待1000毫秒, 看线程是否可以自然结束 if (SubThread.Join(1000) == false) { // 如果等待失败, 则强行退出线程 SubThread.Abort(); } }
**对于在线程中使用Control类的Invoke方法通过委访问行窗体或控件方法或属性的情况,还有几点注意事项:
I/ 窗体不能先于线程销毁。当线程代码运行到Invoke方法时,如果窗体已经被关闭,则会引发异常。此时,窗体必须等待并确保线程确实退出了才能关闭;
II/ 不能在创建窗体的线程(一般为主线程)中等待辅助线程结束(调用线程对象的Join方法),因为调用了Join方法的线程会被阻塞(即不再向下运行,而在Join方法上等待),而Invoke方法会向窗体所在线程的消息循环发送执行委托的消息,窗体线程被阻塞后消息循环也会被阻塞(除非调用:Application.DoEvents();//主动调用消息循环 ),Invoke方法自然也会被阻塞,结果就是窗体线程和辅助线程均被阻塞,程序进入死锁;
!!!Delegate.Invoke是相当于直接调用。
!!!Control.Invoke是SendMessage,将当前操作同步到界面线程的。 所以我们在Control.Invoke的代理函数尽量不要使用阻滞线程的操作,因为当前线程是主线程。
III/ 解决上述问题的方法很简单:在窗体关闭事件中取消窗体关闭(e.Cancel)并启动一个辅助线程来等候其它辅助线程结束(第64-80行),并在等候成功后由该线程通过委托方法来关闭窗体。用辅助线程来等待其它辅助线程结束,则不会阻塞窗体线程,等待成功后关闭窗体,则可以保证窗体在这些线程退出后才安全关闭。
在用c#做WinFrom开发的过程中。我们经常需要用到进度条(ProgressBar)用于显示进度信息。这时候我们可能就需要用到多线程,如果不采用多线程控制进度条,窗口很容易假死(无法适时看到进度信息)。下面我就简单结合一个我写的例子给大家做一个介绍。 第一步:设计界面不说了...注意需要引用 using System.Threading; 第二步:定义一个代理,用于更新ProgressBar的值(Value) //更新进度列表 private delegate void SetPos(int ipos); 第三步:进度条值更新函数(参数必须跟声明的代理参数一样) private void SetTextMessage(int ipos) { if (this.InvokeRequired) { SetPos setpos = new SetPos(SetTextMessage); this.Invoke(setpos, new object[] { ipos}); } else { this.label1.Text = ipos.ToString() + "/100"; this.progressBar1.Value = Convert.ToInt32(ipos); } } 第四步:函数实现 private void button1_Click(object sender, EventArgs e) { Thread fThread = new Thread(new ThreadStart(SleepT));//开辟一个新的线程 fThread.Start(); } 第五步:新的线程执行函数: private void SleepT() { for (int i = 0; i < 500; i++) { System.Threading.Thread.Sleep(100);//没什么意思,单纯的执行延时 SetTextMessage(100 * i / 500); } } 到此一个简单的进度条程序做好了 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/xiven/archive/2009/12/04/4943003.aspx
设置前台线程和后台线程
前台线程(Thread类默认为前台线程):即该线程会阻止进程结束。即便主线程结束了,只要进程中还有一个前台线程尚未结束,则进程不会结束,直到所有的前台线程都结束。这是最安全的一种方式,保证线程在结束前做完其所有工作。
后台线程(不涉及I/O操作的线程)-既IsBackground=True :只要主线程退出,这些线程就会被强制结束。
http://msdn.microsoft.com/zh-cn/library/7a2f3ay4(VS.80).aspx
------------------------
BackgroundWorker--后台操作控件
★BackgroundWorker实现WinForm异步操作/演练:在后台运行操作
如果有一个需要很长时间才能完成的操作,而且不希望用户界面中出现延迟,则
可以使用
BackgroundWorker 类来在另一个线程上运行该操作。
事件:
1,ReportProgress的调用将会导致ProgressChanged事件被触发。
ProgressChanged event handler用于
显示当前进度、当前记录数量和显示获取的纪录:
2,如何操作正常地结束,BackgroundWorker的RunWorkerCompleted会被触发
注:如果操作需要操作UI上的控件,只能在Main Thread中进行。如何在
RetrieveData方法进行的话,由
于该方式是一个异步方法,是会抛出异常的。
方法:
1,this.backgroundWorkerLeft.RunWorkerAsync(object);//开始
RunWorkerAsync方法被掉调用,我们定义的常量(object )当作参数被掺入。随
后,将会触发其DoWork
事件
this.backgroundWorkerLeft.CancelAsync();//手工结束掉当前的操作
------------------------------------------------------------------------
多线程 实现返回值 任意参数
1,使用委托异步
2,
// The ThreadWithState class contains the information needed for // a task, and the method that executes the task. // //ThreadStart 委托既没有参数也没有返回值。这意味着不可以使用需要参数的方法启动线程,或从方法中获得返回值。 //为向线程传递数据,需要创建一个用来保持数据和线程方法的对象,如下面的两个代码示例所示。 //为检索线程方法的结果,您可以使用回调方法,如第二个代码示例中所示。 public class ThreadWithState { // State information used in the task. private string boilerplate; private int value; // The constructor obtains the state information. public ThreadWithState(string text, int number) { boilerplate = text; value = number; } // The thread procedure performs the task, such as formatting // and printing a document. public void ThreadProc() { Console.WriteLine(boilerplate, value); } } public class Example { public static void Main() { // Supply the state information required by the task. ThreadWithState tws = new ThreadWithState("This report displays the number {0}.", 42); // Create a thread to execute the task, and then // start the thread. Thread t = new Thread(new ThreadStart(tws.ThreadProc)); t.Start(); Console.WriteLine("Main thread does some work, then waits."); t.Join(); Console.WriteLine("Independent task has completed; main thread ends."); } } // Entry point for the example. // //用回调方法检索数据 //下面的示例演示了一个从线程中检索数据的回调方法。包含数据和线程方法的类的构造函数也接受代表回调方法的委托;在线程方法结束前,它调用该回调委托。 // The ThreadWithState class contains the information needed for // a task, the method that executes the task, and a delegate // to call when the task is complete. // public class ThreadWithState { // State information used in the task. private string boilerplate; private int value; // Delegate used to execute the callback method when the // task is complete. private ExampleCallback callback; // The constructor obtains the state information and the // callback delegate. public ThreadWithState(string text, int number, ExampleCallback callbackDelegate) { boilerplate = text; value = number; callback = callbackDelegate; } // The thread procedure performs the task, such as // formatting and printing a document, and then invokes // the callback delegate with the number of lines printed. public void ThreadProc() { Console.WriteLine(boilerplate, value); if (callback != null) callback(1); } } // Delegate that defines the signature for the callback method. // public delegate void ExampleCallback(int lineCount); // Entry point for the example. // public class Example { public static void Main() { // Supply the state information required by the task. ThreadWithState tws = new ThreadWithState( "This report displays the number {0}.", 42, new ExampleCallback(ResultCallback) ); Thread t = new Thread(new ThreadStart(tws.ThreadProc)); t.Start(); Console.WriteLine("Main thread does some work, then waits."); t.Join(); Console.WriteLine("Independent task has completed; main thread ends."); } // The callback method must match the signature of the // callback delegate. // public static void ResultCallback(int lineCount) { Console.WriteLine("Independent task printed {0} lines.", lineCount); } }
http://hi.baidu.com/ccm_8730/blog/item/626f95091c81e82d6a60fb38.html