上篇博客从线程的基本概况开始着重讨论了线程,进程,程序之间的区别,然后讨论了线程操作的几个类,并通过实例来说明了线程的创建方法。本篇博客将会带大家更深入的了解线程,介绍线程的基本方法,并通过一个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. raindi python魔法函数(一)之__repr__与__str__

    __repr__和__str__都是python中的特殊方法,都是用来输出实例对象的,如果没有定义这两个方法在打印的时候只会输出实例所在的内存地址 这种方式的输出没有可读性,并不能直观的体现实例.py ...

  2. 多CPU,多核,多进程,多线程

    当面临这些问题的时候,有两个关键词无法绕开,那就是并行和并发. 首先,要先了解几个概念: 1.进程是程序的一次执行. 2.进程是资源分配的基本单位(调度单位). 3.一个进程可以包括多个线程. 4.在 ...

  3. java :: Java中的双冒号操作符

    java中的双冒号操作符 定义 双冒号运算操作符是类方法的句柄,lambda表达式的一种简写,这种简写的学名叫eta-conversion或者叫η-conversion. 通常的情况下: 把 x -& ...

  4. hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询

    点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...

  5. 《Java程序性能优化》之并发优化

    第四章 并行程序优化 1.非阻塞同步避免了基于锁的同步的缺陷,无锁算法没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销.CAS算法:包含3个参数CAS(v,e,n).V表示要更新的变量,E表示 ...

  6. zjoi2017 仙人掌

    题解: 好难的dp啊...看题解看了好久才看懂 http://blog.csdn.net/akak__ii/article/details/65935711 如果一开始的图就不是仙人掌,答案显然为0, ...

  7. asp.net core2.0大白话带你入门

    本系列包括: 1.新建asp.net core项目2.web项目目录解读3.配置访问地址4.环境变量详解5.配置文件6.日志7.DI容器8.服务的生命周期9.session的使用10.cookie的使 ...

  8. fork调用的底层实现

    fork调用的内核实现: http://www.cnblogs.com/huangwei/archive/2010/05/21/1740794.html http://blog.csdn.net/he ...

  9. AngularJS表格神器“ui-grid”的应用

    HTML:  (代码仅用于解释得更清楚,并未完全展示) <!doctype html> <html ng-app="app"> <head> & ...

  10. 全排列-hdu1716

    题目描述: 题目意思很简单,就是要我们输出全排列后的数据组成,但是要注意组成的数据是一个实数,并且千位数字相同的处在同一行中. 代码实现: #include<stdio.h> #inclu ...