.NET简谈组件程序设计之(初识.NET线程Thread)

简介: 由于多线程的内容比较多我会用几篇文章来讲解。 多线程在我们日常开发过程中用的很多,上一篇“.NET简谈组件程序设计之(异步委托) ”详细的讲解了基于委托的多线程使用,委托是基于后台线程池的原理,这篇文章将主要介绍直接使用Thread对象来实现多线程。

由于多线程的内容比较多我会用几篇文章来讲解。

多线程在我们日常开发过程中用的很多,上一篇“.NET简谈组件程序设计之(异步委托) ”详细的讲解了基于委托的多线程使用,委托是基于后台线程池的原理,这篇文章将主要介绍直接使用Thread对象来实现多线程。

当然使用Thread没有使用Delegate那么容易,毕竟多线程跟异步调用是两个相差很大的技术方向,我也是略懂点皮毛,在此献丑给大家,如有讲的不对的地方还请指出。[王清培版权所有,转载请给出署名]

我们先来理解几个概念,以方便我们学习。

后台线程与前台线程

前台线程:什么叫前台线程,就是我们使用默认的Thread创建出来的没有进行IsBackground属性设置的都是前台线程,因为默认IsBackground是false。前台线程是明确任务的,也就是任何一个前台线程没有结束之前程序是不会自动退出的,除非强制关闭应用程序。

后台线程:后台线程是针对前台线程来说的,将Thread.IsBackground设置为true就是后台线程,后台线程是为前台线程服务的,就是说后台线程没有很强的生命力,只要前台线程都结束了,后台线程都强制结束,哪怕任务还没有完成都不行。所以我们在使用的时候要看情况进行选择。[王清培版权所有,转载请给出署名]

线程的切换

我们来看一段代码,以方便引入主题。

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Threading;
using System.Runtime.Remoting; 
 using System.Runtime.Remoting.Contexts;  
 namespace ConsoleApplication1.多线程和并发管理 
 {     
	public class MyClass     
 	{    
     	 	public void ShowMessage()      
   	 	{             
 			Thread currentthread = Thread.CurrentThread;            
 	 		Console.WriteLine(currentthread.Name + currentthread.ManagedThreadId);       
   		}               
 	} 
 }  

 

这是一段很简单的代码,就是一个ShowMessage方法,在方法里面有一个获取当前上下文线程的静态属性Thread.CurrentThread,然后输入该线程的名称和托管ID号;

namespace ConsoleApplication1.多线程和并发管理 
 {     
 public static class Program   
   {         
 	static void Main(string[] args)        
  	{             
 		Thread currentthread = Thread.CurrentThread;         
    	 currentthread.Name = "主线程";         
    	 Console.WriteLine(currentthread.Name + currentthread.ManagedThreadId);       
        MyClass myclass = new MyClass();           
   	ThreadStart start = new ThreadStart(myclass.ShowMessage);         
    	Thread thread = new Thread(start);           
   	thread.Name = "子线程";            
  	thread.Start();              
 	Thread.Sleep(1000);//休眠,线程切换           
   	Console.WriteLine(currentthread.Name + currentthread.ManagedThreadId);         
     	Console.Read();   
   }     
 }    
 }  

 

这是调用代码,我先给主线程起个名字,然后输出。我新建了一个thread线程,这是子线程,调用我们上面定义的方法,用同样的Thread.CurrentThread来获取当前上下文线程,最后让主线程休眠一秒钟。

通过该图我们能清楚的看见,系统在后台自动帮我们进行线程切换,用同一个静态变量就可以获取到当前线程对象。

通过Sleep方法是让当前线程休眠指定的时间断,哪怕当前线程正在CPU上运行着,一旦调用Sleep就立刻放弃CPU给它的时间片,进入阻塞状态。

 [一个线程仅仅是一个进程中的执行路径]

其实线程是执行路径,系统中维护着一个执行路径的命令集合,当我们开启了多个线程的时候其实就是往着个命令集合中存放了很多要执行的命令而已,换句话说命令就是线程队列,用CPU 对它进行时间片的执行。

那么线程是肯定需要一系列的状态的,这个状态时有OS帮我们维持着,因为线程是属于内核层的对象,只有OS才能实时监控着。我们只需要用就行了,有兴趣的朋友可以参考,杰夫瑞 (Jeffrey Richter)《Windows核心编程(第5版)》  一书。

让线程等待而不是切换

Sleep是强制放弃CPU的时间片,然后重新和其他线程一起参与CPU的竞争。用Sleep是会让线程放弃CPU的使用权,而如果我们换成  Thread.SpinWait(100000000),是不会放弃CPU的使用权的,只是让CPU去执行一段没有用的代码,当时间结束之后能立马继续执行,而不是和重新参与CPU的竞争。

在系统资源很丰富的情况下可能这点并不重要,但是在资源缺乏,CPU又不是很好的时候,我想这点还是能改善点性能的。

在此不得不提一个重要的概念,就是线程的调用方和线程主体,线程的调用方就是线程的客户端,是另外一个线程,而不是当前线程主体。

Thread.Join()连接线程

join方法从字面理解是连接的意思,刚接触真的很难理解,什么叫连接。请看一段代码:

Thread currentthread = Thread.CurrentThread;     
currentthread.Name = "主线程";        
Console.WriteLine(currentthread.Name + currentthread.ManagedThreadId);      
MyClass myclass = new MyClass();          
ThreadStart start = new ThreadStart(myclass.ShowMessage);        
Thread thread = new Thread(start);          
thread.Name = "子线程";          
thread.Start();         
thread.Join();//阻塞子线程thread线程,直到子线程thread执行结束        
Console.WriteLine(currentthread.Name + currentthread.ManagedThreadId);       
Console.Read(); 

 

通过Join我们可以等待线程结束,连接的意思就是将我和我调用的thread线程连接起来,我要等你结束之后我才能继续执行,这里就是主线程和子线程的关系,只有子线程技术之后,主线程才能继续执行。

Thread.Abort终止线程

利用Abort可以终止一个在执行的线程,但是Abort会在线程上引发一个ThreadAbortException异常。

public void DoWork()    
{           
     try          
   {             
      int i = 0;       
      while (true)      
      {           
          Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "|" + i++);        
             if (i == 100)           
     	     {                        
 		Console.WriteLine("---------------------------------------------");          
                Console.Read();       
                break;//退出当前线程执行,尽量不要用Abort结束       
              }         
        }          
   }           
  catch (ThreadAbortException err)         
        {           
          Console.WriteLine(err.Message + "11");    
         }      
} 

 

Thread currentthread = Thread.CurrentThread;     
 Console.WriteLine(currentthread.Name + currentthread.ManagedThreadId);      
 MyClass myclass = new MyClass();         
ThreadStart start = new ThreadStart(myclass.DoWork);      
 Thread thread = new Thread(start);          
thread.Start();            
Thread.Sleep(5000);          
Console.WriteLine(currentthread.Name + currentthread.ManagedThreadId + "是否终止子线程");       
 thread.Abort();       
Console.Read(); 

 

Thread.IsBackground = true后台线程

通过设置IsBackground可以让线程处于后台线程,只要前台线程结束,那么后台线程自动终止。

public void DoWork()      
   {           
  	try          
  	{            
     	      int i = 0;      
              while (true)   
              {                
    		Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "|" + i++);        
            	 //if (i == 100)        
             	//{                  
   		//    Console.WriteLine("---------------------------------------------");            
        	 //    Console.Read();            
        	 //    break;//退出当前线程执行,尽量不要用Abort结束           
         	 //}            
    	      }            
 	}           
  	catch (ThreadAbortException err)       
     	 {      
          	 Console.WriteLine(err.Message + "11");       
      	} 

 

 

我们将一段代码注释掉。

Thread currentthread = Thread.CurrentThread;     
Console.WriteLine(currentthread.Name + currentthread.ManagedThreadId);      
MyClass myclass = new MyClass();           
ThreadStart start = new ThreadStart(myclass.DoWork);     
Thread thread = new Thread(start);    
thread.IsBackground = true;      
thread.Start();           
Thread.Sleep(2000);           
Console.WriteLine(currentthread.Name + currentthread.ManagedThreadId); 

 

 

 

这是调用代码。只要前台线程不结束,后台线程就一直执行。如果我们在最后加上一段Console.ReadLine();代码,那么后台线程会始终运行着。

 

这篇文章就先结束了,下一篇我们将学习关于同步域和上下文的概念。 

 

 

 

目录
相关文章
|
2月前
|
Java 开发者
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
34 0
|
1月前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
44 7
|
15天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
【多线程面试题 二】、 说说Thread类的常用方法
Thread类的常用方法包括构造方法(如Thread()、Thread(Runnable target)等)、静态方法(如currentThread()、sleep(long millis)、yield()等)和实例方法(如getId()、getName()、interrupt()、join()等),用于线程的创建、控制和管理。
|
1月前
|
SQL 机器学习/深度学习 算法
【python】python指南(一):线程Thread
【python】python指南(一):线程Thread
36 0
|
2月前
|
Java C# Python
线程等待(Thread Sleep)
线程等待(Thread Sleep)
|
3月前
|
存储 设计模式 安全
C++一分钟之-并发编程基础:线程与std::thread
【6月更文挑战第26天】C++11的`std::thread`简化了多线程编程,允许并发执行任务以提升效率。文中介绍了创建线程的基本方法,包括使用函数和lambda表达式,并强调了数据竞争、线程生命周期管理及异常安全等关键问题。通过示例展示了如何用互斥锁避免数据竞争,还提及了线程属性定制、线程局部存储和同步工具。理解并发编程的挑战与解决方案是提升程序性能的关键。
64 3
|
3月前
|
Java
Java中,有两种主要的方式来创建和管理线程:`Thread`类和`Runnable`接口。
【6月更文挑战第24天】Java创建线程有两种方式:`Thread`类和`Runnable`接口。`Thread`直接继承受限于单继承,适合简单情况;`Runnable`实现接口可多继承,利于资源共享和任务复用。推荐使用`Runnable`以提高灵活性。启动线程需调用`start()`,`Thread`直接启动,`Runnable`需通过`Thread`实例启动。根据项目需求选择适当方式。
47 2
|
3月前
|
Java 开发者
JAVA多线程初学者必看:为何选择继承Thread还是Runnable,这其中有何玄机?
【6月更文挑战第19天】在Java中创建线程,可选择继承Thread类或实现Runnable接口。继承Thread直接运行,但限制了多重继承;实现Runnable更灵活,允许多线程共享资源且利于代码组织。推荐实现Runnable接口,以保持类的继承灵活性和更好的资源管理。
45 2
|
3月前
|
Java 开发者
告别单线程时代!Java 多线程入门:选继承 Thread 还是 Runnable?
【6月更文挑战第19天】在Java中,面对多任务需求时,开发者可以选择继承`Thread`或实现`Runnable`接口来创建线程。`Thread`继承直接但限制了单继承,而`Runnable`接口提供多实现的灵活性和资源共享。多线程能提升CPU利用率,适用于并发处理和提高响应速度,如在网络服务器中并发处理请求,增强程序性能。不论是选择哪种方式,都是迈向高效编程的重要一步。
33 2