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

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

线程

多线程优缺点

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

线程基本方法

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

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

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

 

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

Demo1:线程,方法--委托

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

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

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

  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方法的灵活运行能够实现线程之间的执行顺序。

  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使用了线程的基本方法,能够更加深刻的了解它们的使用。下篇博客将会更加深入的讨论线程和线程之间的调用关系,以及如何实现线程间的数据传递及检索。

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

  1. MySQL服务器线程数的查看方法详解

    本文实例讲述了MySQL服务器线程数的查看方法.分享给大家供大家参考,具体如下: mysql重启命令: ? 1 /etc/init.d/mysql restart MySQL服务器的线程数需要在一个合 ...

  2. (二)线程Thread中的方法详解

    1.start() start()方法的作用讲得直白点就是通知"线程规划器",此线程可以运行了,正在等待CPU调用线程对象得run()方法,产生一个异步执行的效果.通过start( ...

  3. 线程Thread中的方法详解(二)

    1.start() start()方法的作用讲得直白点就是通知"线程规划器",此线程可以运行了,正在等待CPU调用线程对象得run()方法,产生一个异步执行的效果.通过start( ...

  4. “全栈2019”Java多线程第十二章:后台线程setDaemon()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  5. “全栈2019”Java多线程第七章:等待线程死亡join()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. “全栈2019”Java多线程第六章:中断线程interrupt()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. “全栈2019”Java多线程第五章:线程睡眠sleep()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  8. java多线程并发(二)--线程的生命周期及方法详解

    上篇随笔介绍了线程的相关基础知识以及新启线程的几种方法,本片将继续介绍线程的生命周期及方法详解. 一.线程的生命周期 在Thread代码中,线程的状态被分为6种 public enum State { ...

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

    一.notify() 作用:唤醒一个正在等待该线程的锁的线程 PS : 唤醒的线程不会立即执行,它会与其他线程一起,争夺资源 /** * Object类的notify()和notifyAll()方法详 ...

  10. java线程池的使用与详解

    java线程池的使用与详解 [转载]本文转载自两篇博文:  1.Java并发编程:线程池的使用:http://www.cnblogs.com/dolphin0520/p/3932921.html   ...

随机推荐

  1. Linear Algebra(未完待续)

    [矩阵消元] The result of multiplying a matrix by some vector is a combination of the columns of the matr ...

  2. linux 下 eclipse 安装

    下载: 官网选择相应安装包下载,我这里下了tar.gz包 安装: tar xzvf eclipse-inst-linux64.tar.gz 设置环境变量 export JAVA_HOME=/usr/l ...

  3. 关于Bootstrap自定义图标

    1.网站:http://fontello.com/(可能是网站本身原因,总是加载很慢,需要等待一下) 2.eps格式即AI文件,所需画布大小是512*512的,且需要布满整个画布,否则存储出来的图标显 ...

  4. (String中)正则表达式使用如下

    package zhengze;/* * 正则表达式 */public class StringTestZhengZe { public static void main(String[] args) ...

  5. ***LANMP镜像手册(Apache&Nginx)-lanmp-oneinstack

    LANMP镜像手册(Apache&Nginx) Version PHP7.0.26  转自:http://docs.websoft9.com/xdocs/lanmp-oneinstack-im ...

  6. PTA之求单链表结点的阶乘和

    本题要求实现一个函数,求单链表L结点的阶乘和.这里默认所有结点的值非负,且题目保证结果在int范围内. 时间限制: 400ms 内存限制: 64MB 代码长度限制: 16KB 函数接口定义: int ...

  7. sublime断点调试

    系统重装了之后以前装好的zendstudio的xDebug又不能用了 搞了一天,放弃了 看到sublime也能调试,我就用sublime了 首先要下sublime插件 链接:http://pan.ba ...

  8. CPU个数、CPU核心数、CPU线程数

    CPU个数.CPU核心数.CPU线程数 我们在选购电脑的时候,CPU是一个需要考虑到核心因素,因为它决定了电脑的性能等级.CPU从早期的单核,发展到现在的双核,多核.CPU除了核心数之外,还有线程数之 ...

  9. Shell学习之Shell特性(一)

    Shell学习之Shell特性 目录 命令和文件自动补齐功能 命令历史记忆功能 history.上下键.!number.!string.!$.!! 别名功能 alias.unalias cp.~use ...

  10. python的random模块(生成验证码)

    python的random模块(生成验证码) random模块常用方法 random.random() #生成0到1之间的随机数,没有参数,float类型 random.randint(1, 3) # ...