《CLR Via C# 第3版》笔记之(十七) - 线程基础

简介:

最近项目中要用到线程相关的知识,所以先看了本书后面线程的内容,并做了一些总结。

主要内容:

  • 线程开销
  • 线程的创建
  • 前台和后台线程 

1. 线程开销

1.1 线程的概念

如果没有线程,当应用程序出现死循环时,应用程序所在的进程会一直占据CPU,导致"死机"的现象。

那么线程是如何避免这种"死机"现象,使得应用程序能更好的响应用户的请求呢?

Windows系统中引入了线程的概念后,每个进程至少有一个专有线程(相当于这个进程专用的CPU),

系统已线程为单位分配CPU时间片,如果一个应用程序进入无限循环,那么它的专有线程会"死机"。

但是其它进程的专有线程不会"死机",所以系统不会停止响应,也不会出现"死机"。

 

1.2 线程的结构

线程还可以增加程序的并发性,对于多CPU的场合,能够提高程序的性能,但同时也会使程序更加复杂。

相对于进程,线程确实是很"轻量",但是如果在进程中蛮目的增加线程,同样会对系统资源带来很大的负担。

下面来看看Windows系统中线程的开销有多大?

线程主要包含以下几个要素:

线程内核对象。x86(约700字节),x64(约1240字节),IA64(约2500字节)

包含线程的属性及上下文信息,其中上下文信息包含当前CPU的寄存器信息等。

当CPU切换线程时,需要将当前线程的寄存器信息保存到上下文中,同时将新线程的上下文复制到CPU寄存器中。

 

线程环境块。x86和x64(4KB),IA64(8KB)

在用户模式中分配,包含线程的异常处理链首,线程本地存储的数据,GDI和OpenGL图形使用的一些数据结构。

 

用户模式栈。1MB

存储方法的实参和局部变量,以及返回的地址。

 

内核模式栈。32bit(12KB),64bit(24KB)

与内核交互时的数据(比如传递给内核的参数)。

 

DLL的attach和detach通知。

创建线程时,调用当前进程加载的所有DLL的DllMain方法,并传递DLL_THREAD_ATTACH标志。

终止线程时,调用当前进程加载的所有DLL的DllMain方法,并传递DLL_THREAD_DETACH标志。

 

通过上面的分析,我们发现每个线程至少占用1MB的内存,资源的消耗并不小,所有要理性的使用它们,只在必须要用线程的地方使用它们。

 

2. 线程的创建

C#中使用线程非常简单,利用System.Threading.Tread类即可。

Thread的构造函数的参数有2种委托:

(1) 一种是有参数的委托

1
public  delegate  void  ParameterizedThreadStart( object  obj);

(2) 一种是无参数的委托

1
public  delegate  void  ThreadStart()

 

例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using  System;
using  System.Threading;
 
class  CLRviaCSharp_17
{
     static  void  Main( string [] args)
     {
         Console.WriteLine( "This is Main Thread!" );
         
         // 有参数的委托
         Thread t1 = new  Thread(SubThreadMethod);
         t1.Start( "sub thread parameter" );
         
         // 无参数的委托
         Thread t2 = new  Thread(SubThreadMethod2);
         t2.Start();
 
         Thread.Sleep(4000);
         Console.WriteLine( "Main Thread complete!" );
         Console.ReadKey( true );
     }
 
     private  static  void  SubThreadMethod( object  param)
     {
         Console.WriteLine( "This is Sub Thread with parameter : {0}" , param);
         Thread.Sleep(1000);
     }
 
     private  static  void  SubThreadMethod2()
     {
         Console.WriteLine( "This is Sub Thread without parameter!" );
         Thread.Sleep(1000);
     }
}

 

其中Thread t1和t2的执行顺序是不定的,可能t1先执行,也可能t2先执行。

 

3. 前台和后台线程

虽然目前一个CLR中的线程直接对应于一个Windows中的线程,但是以后是有可能分离的。

而我们目前在C#中使用的线程都是CLR线程。

CLR线程分为前台线程和后台线程2种。上面例子中直接创建的线程默认为前台线程,用线程池创建的线程默认为后台线程。

我们应尽量避免使用前台线程,多使用后台线程。

原因在于:一个进程只有当它的所有前台线程全部终止后才会终止,如果有一个前台线程陷入死循环,那么这个进程就无法自动终止。

下面来看我们验证的例子:

首先验证前台线程,新建100个前台线程,每个线程Sleep 5秒,主线程在新建完100个线程后就结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using  System.Threading;
 
class  CLRviaCSharp_17
{
     static  void  Main( string [] args)
     {
         for  ( int  i = 0; i < 100; i++)
         {
             Thread t = new  Thread(ThreadMethod);
             t.Start();
         }
     }
 
     private  static  void  ThreadMethod()
     {
         Thread.Sleep(5000);
     }
}

程序运行后并没有立即结束,而是等了5秒才结束。(原因在于有前台线程没终止,进程无法自动终止)

 

再验证后台进程,代码差不多,只是在新建线程后把线程的IsBackground属性改为True

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using  System.Threading;
 
class  CLRviaCSharp_17
{
     static  void  Main( string [] args)
     {
         for  ( int  i = 0; i < 100; i++)
         {
             Thread t = new  Thread(ThreadMethod);
             // 将新建的线程标记为后台线程
             t.IsBackground = true ;
             t.Start();
         }
     }
 
     private  static  void  ThreadMethod()
     {
         Thread.Sleep(5000);
     }
}

程序运行后立即结束,没有等后台线程5秒。(原因在于所有前台线程已终止,后台进程也自动终止了)



本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/10/28/2227846.html,如需转载请自行联系原作者


目录
相关文章
|
4月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
173 3
|
4月前
|
安全 编译器 C#
C#学习相关系列之多线程---lock线程锁的用法
C#学习相关系列之多线程---lock线程锁的用法
|
4月前
|
Java 调度 C#
C#学习系列相关之多线程(一)----常用多线程方法总结
C#学习系列相关之多线程(一)----常用多线程方法总结
|
4月前
|
C#
C#学习相关系列之多线程---ConfigureAwait的用法
C#学习相关系列之多线程---ConfigureAwait的用法
|
4月前
|
C#
C#学习相关系列之多线程---TaskCompletionSource用法(八)
C#学习相关系列之多线程---TaskCompletionSource用法(八)
|
18天前
|
数据采集 XML JavaScript
C# 中 ScrapySharp 的多线程下载策略
C# 中 ScrapySharp 的多线程下载策略
|
23天前
|
安全 C# 开发者
【C# 多线程编程陷阱揭秘】:小心!那些让你的程序瞬间崩溃的多线程数据同步异常问题,看完这篇你就能轻松应对!
【8月更文挑战第18天】多线程编程对现代软件开发至关重要,特别是在追求高性能和响应性方面。然而,它也带来了数据同步异常等挑战。本文通过一个简单的计数器示例展示了当多个线程无序地访问共享资源时可能出现的问题,并介绍了如何使用 `lock` 语句来确保线程安全。此外,还提到了其他同步工具如 `Monitor` 和 `Semaphore`,帮助开发者实现更高效的数据同步策略,以达到既保证数据一致性又维持良好性能的目标。
25 0
|
3月前
|
关系型数据库 C# 数据库
技术笔记:MSCL超级工具类(C#),开发人员必备,开发利器
技术笔记:MSCL超级工具类(C#),开发人员必备,开发利器
38 3
|
3月前
|
并行计算 算法 C#
C# Mandelbrot和Julia分形图像生成程序更新到2010-9-14版 支持多线程计算 多核处理器
此文档是一个关于分形图像生成器的介绍,作者分享了个人开发的M-J算法集成及色彩创新,包括源代码和历史版本。作者欢迎有兴趣的读者留言交流,并提供了邮箱(delacroix_xu@sina.com)以分享资源。文中还展示了程序的发展历程,如增加了真彩色效果、圈选放大、历史记录等功能,并分享了几幅精美的分形图像。此外,还提到了程序的新特性,如导入ini文件批量输出图像和更新一批图片的功能。文档末尾附有多张程序生成的高分辨率分形图像示例。