.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();代码,那么后台线程会始终运行着。

 

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

 

 

 

目录
相关文章
|
5月前
|
Java 开发者
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
60 0
|
4月前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
62 7
|
24天前
|
Java C# Python
线程等待(Thread Sleep)
线程等待是多线程编程中的一种同步机制,通过暂停当前线程的执行,让出CPU时间给其他线程。常用于需要程序暂停或等待其他线程完成操作的场景。不同语言中实现方式各异,如Java的`Thread.sleep(1000)`、C#的`Thread.Sleep(1000)`和Python的`time.sleep(1)`。使用时需注意避免死锁,并考虑其对程序响应性的影响。
|
1月前
|
Java 网络安全 Maven
Exception in thread "main" java.lang.NoSuchMethodError: okhttp3.OkHttpClient$Builder.sslSocketFactory(Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder; 问题处理
【10月更文挑战第26天】Exception in thread "main" java.lang.NoSuchMethodError: okhttp3.OkHttpClient$Builder.sslSocketFactory(Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder; 问题处理
43 2
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
27 3
|
2月前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
51 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
38 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
44 1
|
3月前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
【多线程面试题 二】、 说说Thread类的常用方法
Thread类的常用方法包括构造方法(如Thread()、Thread(Runnable target)等)、静态方法(如currentThread()、sleep(long millis)、yield()等)和实例方法(如getId()、getName()、interrupt()、join()等),用于线程的创建、控制和管理。