【.NET线程--进阶(一)】--线程方法详解

上篇博客从线程的基本概况开始着重讨论了线程,进程,程序之间的区别,然后讨论了线程操作的几个类,并通过实例来说明了线程的创建方法。本篇博客将会带大家更深入的了解线程,介绍线程的基本方法,并通过一个Demo使用委托来调用线程之外的对象。

前篇博客基础:【GDI+编程--番外篇(二)】--从事件看委托
                               【.NET线程--开篇】--线程从零开始

线程

多线程优缺点

多线程的使用会帮助程序提高响应速度,因为可以同时执行多个任务这样对比一个个的来完成任务来说提高了响应的速度,较之添加多CPU来说多线程提高了强大的技术来执行多个任务。虽然多线程提高了响应速度,但同时牺牲了资源,由于多线程的执行它会占用多个资源,为了避免资源访问的冲突,往往会在每个线程中都会创建自己的资源,这样导致了资源的浪费。另外如果线程过多,则其中大多数线程都不会产生明显的进度,如果大多数当前线程处于一个进程中,则其他进程中的线程的调度频率就会很低。

线程基本方法

下表包括了在线程编程过程中常用的基本方法。

可用于控制单个线程的方法

方法 操作
Start  使线程开始运行。
Sleep 使线程暂停指定的一段时间。
Suspend 在线程到达安全点时,使其暂停。
Abort  在线程到达安全点时,使其停止。
Resume 重新启动挂起的线程
Join 使当前线程一直等到另一线程完成。 在与超时值一起使用时,如果该线程在分配的时间内完成,此方法将返回 True。

 

       Note: 安全点是指代码中公共语言运行时可以安全地执行自动“垃圾回收”的位置。垃圾回收是指释放不再使用的变量并回收内存的过程。 调用线程的 Abort 或 Suspend 方法时,公共语言运行时将对代码进行分析,确定让线程停止运行的适当位置。

Demo1:线程,方法--委托

自己做的一个小Demo来实现多线程,当点击开始按钮后会在文本框中填写数字,与此同时加载进度条,读取进度,点击暂停后线程会停止。另外可以在文本框中输入暂停时间来指定线程暂停时间,在暂停后继续执行。

Demo下载地址:线程常用方法示例

在点击开始按钮后会同时创建两个线程,分别为showNumThread和pBarThread,用来向文本框中写入数字和加载进度条。这里需要说明的是,一般情况下线程内部是不允许调用线程外创建的对象的,创建的两个线程都调用了线程外部的对象,是怎么实现的呢?使用的是委托来异步执行程序来实现了调用线程外部的方法。

[csharp] view plain copy

  1. /// <summary>
  2. /// 开始按钮事件,创建线程并为线程指定方法
  3. /// </summary>
  4. /// <param name="sender"></param>
  5. /// <param name="e"></param>
  6. private void btnStart_Click(object sender, EventArgs e)
  7. {
  8. pBarThread = new Thread(new ThreadStart(this.ExepBarShow)); //创建进度条线程
  9. showNumThread = new Thread(new ThreadStart(this.ExeShowNum));   //创建显示文本框中的文字线程
  10. //开始两个已创建的线程
  11. this.StartThread(showNumThread);
  12. this.StartThread(pBarThread);
  13. }
  14. /// <summary>
  15. /// 使用委托执行ShowNumToText方法
  16. /// </summary>
  17. private void ExeShowNum()
  18. {
  19. try
  20. {
  21. MethodInvoker mInvoker = new MethodInvoker(this.ShowNumToText); //声明托管委托,并为委托执行执行的方法
  22. //执行委托方法,向Text中写入文字
  23. while (true)
  24. {
  25. this.BeginInvoke((Delegate)mInvoker);   //异步执行执行的委托
  26. Thread.Sleep(1000);     //线程停顿1秒后继续执行
  27. }
  28. }
  29. catch { }
  30. }
  31. /// <summary>
  32. /// 先文本框txtNum中写入文字
  33. /// </summary>
  34. private void ShowNumToText()
  35. {
  36. i = i + 1;  //i累加
  37. txtNum.Text = txtNum.Text + " " + (i + 1).ToString();   //向txtNum中写入文字
  38. }
  39. /// <summary>
  40. /// 执行pBarShow方法,加载进度条,让进度条读取进度
  41. /// </summary>
  42. private void ExepBarShow()
  43. {
  44. try
  45. {
  46. MethodInvoker mInvoker = new MethodInvoker(this.pBarShow);  //声明并创建委托,为委托执行进度
  47. //异步执行委托
  48. while (true)
  49. {
  50. this.BeginInvoke((Delegate)mInvoker);
  51. Thread.Sleep(10);
  52. }
  53. }
  54. catch { }
  55. }
  56. /// <summary>
  57. /// 执行进度条读取进度
  58. /// </summary>
  59. private void pBarShow()
  60. {
  61. this.pgBar.PerformStep();
  62. }
  63. /// <summary>
  64. /// 线程开始方法
  65. /// </summary>
  66. /// <param name="th">Thread对象,需要开始的线程</param>
  67. private void StartThread(Thread th) {
  68. th.Start();
  69. }
  70. /// <summary>
  71. /// 线程结束方法
  72. /// </summary>
  73. /// <param name="th">Thread对象,需要结束的线程</param>
  74. private void EndThread(Thread th) {
  75. th.Interrupt(); //中断线程
  76. th.Abort(); //终止线程
  77. th = null;
  78. }
  79. /// <summary>
  80. /// 停止线程事件
  81. /// </summary>
  82. /// <param name="sender"></param>
  83. /// <param name="e"></param>
  84. private void btnStop_Click(object sender, EventArgs e)
  85. {
  86. try
  87. {
  88. this.TestThead();   //验证线程是否存在,如果没有存在将会抛错
  89. this.EndThread(this.pBarThread);    //结束线程
  90. this.EndThread(this.showNumThread); //结束线程
  91. }
  92. catch (Exception ex)
  93. {
  94. //提示错误信息
  95. MessageBox.Show(ex.Message , "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
  96. }
  97. }
  98. /// <summary>
  99. /// 终止线程事件
  100. /// </summary>
  101. /// <param name="sender"></param>
  102. /// <param name="e"></param>
  103. private void btnEnd_Click(object sender, EventArgs e)
  104. {
  105. try
  106. {
  107. this.TestThead();   //验证线程是否创建
  108. this.EndThread(this.pBarThread);//结束线程
  109. this.EndThread(this.showNumThread); //结束线程
  110. txtNum.Text = "";   //清空文本框内容
  111. i = 0;  //数字充值
  112. this.pgBar.Value = 0;//进度条重置
  113. }
  114. catch (Exception ex)
  115. {
  116. //显示错误信息
  117. MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
  118. }
  119. }
  120. /// <summary>
  121. /// 执行指定线程停顿时间
  122. /// </summary>
  123. /// <param name="sender"></param>
  124. /// <param name="e"></param>
  125. private void btnStopMinute_Click(object sender, EventArgs e)
  126. {
  127. try
  128. {
  129. int j = int.Parse(textBox1.Text);   //获取终止的时间
  130. Thread.Sleep(j);    //将线程暂停指定的时间
  131. }
  132. catch (Exception ex)
  133. {
  134. MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
  135. }
  136. }
  137. /// <summary>
  138. /// 验证线程是否存在方法
  139. /// </summary>
  140. private void TestThead() {
  141. if (pBarThread ==null)
  142. {
  143. throw new Exception ("未创建线程,请创建线程后操作!");
  144. }
  145. if (showNumThread  == null)
  146. {
  147. throw new Exception ("未创建线程,请创建线程后操作!");
  148. }
  149. }

Demo2:Join方法使用实例

Join方法能在指定的线程中插入一个线程,当插入的线程执行完成后才会继续执行被插入的线程。.NET为我们重载了此方法,能够为方法传递参数来指定经过的时间,此时该方法的作用与Sleep相类似,执行经过多长时间后来执行被插入的线程。Join方法的灵活运行能够实现线程之间的执行顺序。

[csharp] view plain copy

  1. using System;
  2. using System.Threading;
  3. namespace TestJoin
  4. {
  5. /// <summary>
  6. /// Join方法验证实例,线程t1使用了join方法,线程t2没有使用join方法
  7. /// </summary>
  8. class Program
  9. {
  10. static void Main(string[] args)
  11. {
  12. //创建新线程,为线程执行行为
  13. Thread t1 = new Thread(() =>
  14. {
  15. Thread.Sleep(1000);
  16. Console.WriteLine("t1 is ending.");
  17. });
  18. t1.Start(); //开始线程
  19. t1.Join();  //在主线程中插入t1线程,先执行t1,线程后执行主线程
  20. Console.WriteLine("t1.Join() returned.");   //执行主线程,提示t1已经完成
  21. //创建新线程,为线程执行行为
  22. Thread t2 = new Thread(() =>
  23. {
  24. Thread.Sleep(1000);
  25. Console.WriteLine("t2 is ending.");
  26. });
  27. t2.Start(); //开始线程
  28. Console.WriteLine("t2.Join() returned.");   //执行主线程,提示t1已经完成
  29. Console.ReadLine();
  30. }
  31. }
  32. }
  33. /*输出结果:
  34. *t1 is ending.
  35. *t1.Join() returned.
  36. *
  37. *t2.Join() returned.
  38. *t2 is ending.
  39. */

 输出结果:
               

从输出结果上分析可以得出,Join方法将创建的线程插入到了主线程中当执行完后再继续执行主线程,对应到Demo2中是线程t1插入到了主线程中,这样会首先执行t1线程在控制台上打印“t1 is ending”打印完成后t1线程结束,然后继续执行主线程来打印其它的文字。所以我们完全可以说Join方法是将一个线程插入到主线程中,当执行完插入的线程后再继续执行被插入的线程。

结语

线程的优缺点决定了在开发过程中是否使用多线程,另外灵活运行单线程的方法来实现灵活的控制线程,两个Demo使用了线程的基本方法,能够更加深刻的了解它们的使用。下篇博客将会更加深入的讨论线程和线程之间的调用关系,以及如何实现线程间的数据传递及检索。

时间: 2024-10-25 20:02:12

【.NET线程--进阶(一)】--线程方法详解的相关文章

并发编程(六)Object类中线程相关的方法详解

一.notify() 作用:唤醒一个正在等待该线程的锁的线程 PS : 唤醒的线程不会立即执行,它会与其他线程一起,争夺资源 /** * Object类的notify()和notifyAll()方法详解 */ public class MyNotify { // 在多线程间共享的对象上使用wait private String[] shareObj = {"true"}; public static void main(String[] args) { MyNotify test =

Android——onCreate( )方法详解(转)

android开发之onCreate( )方法详解 onCreate( )方法是android应用程序中最常见的方法之一,那么,我们在使用onCreate()方法的时候应该注意哪些问题呢? 先看看Google Android Developers官网上的解释: onCreate(Bundle) is where you initialize your activity. Most importantly, here you will usually call setContentView(int

Android Service生命周期 Service里面的onStartCommand()方法详解

2014-10-21 23:40 32人阅读 评论(0) 收藏 举报 在Demo上,Start一个Service之后,执行顺序:onCreate - > onStartCommand 然后关闭应用,会重新执行上面两步. 但是把代码拷贝到游戏工程发现,关闭游戏后,只执行了onStart,却没有执行onStartCommand! 查找到下面的文章: [plain] view plaincopy Service里面的onStartCommand()方法详解 启动service的时候,onCreate方

C++调用JAVA方法详解

C++调用JAVA方法详解          博客分类: 本文主要参考http://tech.ccidnet.com/art/1081/20050413/237901_1.html 上的文章. C++调用JAVA主要用到了SUN公司的JNI技术, JNI是Java Native Interface的 缩写.从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互.相关资料见http://java.su

【译】UNIVERSAL IMAGE LOADER. PART 3(四个DisplayImage重载方法详解)

在之前的文章,我们重点讲了Android-Universal-Image-Loader的三个主要组件,现在我们终于可以开始使用它了. Android-Universal-Image-Loader有四个重载方法 void displayImage(String url, ImageView view) void displayImage(String url, ImageView view, DisplayImageOptions options) void displayImage(String

php调用C代码的方法详解和zend_parse_parameters函数详解

http://blog.csdn.net/super_ufo/article/details/3863731 php调用C代码的方法详解 在php程序中需要用到C代码,应该是下面两种情况: 1 已有C代码,在php程序中想直接用 2 由于php的性能问题,需要用C来实现部分功能 针对第一种情况,最合适的方法是用system调用,把现有C代码写成一个独立的程序.参数通过命令行或者标准输入传入,结果从标准输出读出.其次,稍麻烦一点的方法是C代码写成一个daemon,php程序用socket来和它进行

- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate 方法 详解

首先是Run Loop的部分概念,它的作用就是循环.处理事件.具体来说有两个方面: 1. 定时启动任务(一般用和Timer协作):2. 处理事件. 在单线程的app中,不需要注意Run Loop,但不代表没有.程序启动时,系统已经在主线程中加入了Run Loop.它保证了我们的主线程在运行起来后,就处于一种"等待"的状态(而不像一些命令行程序一样运行一次就结束了),这个时候如果有接收到的事件(Timer的定时到了或是其他线程的消息),就会执行任务,否则就处于休眠状态. 如果我们要写多线

“全栈2019”Java多线程第三十章:尝试获取锁tryLock()方法详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多线程第三十章:尝试获取锁tryLock()方法详解 下一章 "全栈2019"Java多线程第三十一章:中断正在等待显式锁的线程 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复

Java AtomicInteger类的使用方法详解_java - JAVA

文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 首先看两段代码,一段是Integer的,一段是AtomicInteger的,为以下: public class Sample1 { private static Integer count = 0; synchronized public static void increment() { count++; } } 以下是AtomicInteger的: public class Sample2 { private s

J2EE进阶(四)Spring配置文件详解

J2EE进阶(四)Spring配置文件详解 前言 Spring配置文件是用于指导Spring工厂进行Bean生产.依赖关系注入(装配)及Bean实例分发的"图纸".Java EE程序员必须学会并灵活应用这份"图纸"准确地表达自己的"生产意图".Spring配置文件是一个或多个标准的XML文档,applicationContext.xml是Spring的默认配置文件,当容器启动时找不到指定的配置文档时,将会尝试加载这个默认的配置文件. 下面列举的是