线程可以处于一个或多个状态,由ThreadState枚举表示。使用Thread类中的一些方法后状态会随之变化。枚举成员如下:
 
      线程的生存期如下:
 

一、线程睡眠
     若线程想要访问的资源不可使用,只能期望隔段时间后,重新尝试讯问资源,这时就要让该线程睡眠等待,线程就会处WaitSleepJoin状态。

Imports System.Threading
Public Class ThreadSleepPublic Shared worker As ThreadPublic Shared worker2 As ThreadPublic Shared Sub Main()Console.WriteLine("Entering the Sub Main!")worker = New Thread(AddressOf Counter)worker2 = New Thread(AddressOf Counter2)worker2.Priority = ThreadPriority.Highestworker.Start()worker2.Start()Console.WriteLine("Exiting the Sub Main!")Console.ReadLine()End SubPublic Shared Sub Counter()Console.WriteLine("Entering Counter")For i As Integer = 1 To 50Console.Write(i & " ")If i = 10 ThenConsole.WriteLine(" worker_线程暂停1秒...")worker.Sleep(1000) '等效于worker.Sleep(TimeSpan.FromSeconds(1))Console.WriteLine("worker线程恢复:")End IfNextConsole.WriteLine()Console.WriteLine("Exiting Counter")End SubPublic Shared Sub Counter2()Console.WriteLine("Entering Countcr2")For i As Integer = 51 To 100Console.Write(i & " ")If i = 70 ThenConsole.WriteLine(" worker2 线程暂停5秒...")worker2.Sleep(5000)Console.WriteLine("worker2线程恢复:")End IfNextConsole. WriteLine()Console.WriteLine("Exiting Counter2")End Sub
End Class

说明:由于worker2线程有较高优先权,所以先执行,至70时暂停5秒;worker较低优先权随后执行,至10时暂停1秒,后继续输出,直到5秒后worder2睡醒,再输出。同时注意,由于线程的优先都是受操作系统调控安排,可以看到worker线程的1、2先输出,继而是worder2的51、52等输出,故优先权并不是绝对意义上的优先。
 

二、中断(唤醒)线程
       线程睡眠时就进入WaitSleepJoin状态,有时有必要主动中断睡眠、唤醒这个线程,唯一就是使用lnterrupt()方法。这样线程就会从睡眠队列进行执行队列。

Imports System.Threading
Public Class InterruptPublic Shared sleeper As ThreadPublic Shared worker As ThreadPublic Shared Sub Main()Console.WriteLine("Entering the Sub Main!")sleeper = New Thread(AddressOf SleepingThread)worker = New Thread(AddressOf AwakeTheThread)sleeper.Start()worker.Start()Console.WriteLine("Exiting the Sub Main!")Console.ReadLine()End SubPublic Shared Sub SleepingThread()For i As Integer = 1 To 50Console.Write(i & " ")If i = 10 Or i = 20 Or i = 30 ThenConsole.WriteLine("Going to sleep at: " & i)Try  'ThreadInterruptedException  在中断的线程中引发,但要在该线程阻塞之后才引发。 sleeper.Sleep(30) '这里易抛异常,用TryCatch ex As Exception '原因见4.2(原因http://blog.csdn.net/wguoyong/article/details/50918322)End TryEnd IfNextEnd SubPublic Shared Sub AwakeTheThread()Dim i As IntegerFor i = 51 To 100Console.Write(i & " ")If sleeper.ThreadState = System.Threading.ThreadState.WaitSleepJoin ThenConsole.WriteLine("Interrupting the sleeping thread")sleeper.Interrupt()End IfNextEnd Sub
End Class

说明:这只是我们想象中让睡眠的线程中断唤醒,但实际上,中断唤醒并不是立即执行,加上线程是由操作系统在调配,更增加了不可控性。上面程序最易出现的就是抛出异常,由于延迟执行中断,中断可能出现在方法AwakeTheThread中任意一段代码中。
 
       线程中睡眠、阻塞、挂起的区别(http://www.cnblogs.com/jason-liu-blogs/archive/2012/12/19/2825202.html)
        注意:.net中线程的挂起Suspend方法与恢复Resume方法已经过时。

三、终止线程
     在调用Thread.Abort方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程。简单地说:它是以抛异常的方式达到终止线程的目的。所以Abort只是抛异常后将线程置为AbortRequested状态,此时并没有真正结束。

四、连接线程
      Join( )方法阻塞线程,直到当前的线程终止。如果一个线程依赖于另一个线程,那么这个方法是很有用的。连接两个线程的意思是,当调用Join( )方法时,运行着的线程将进入WaitSleepJoin状态,而直到调用Join( )方法的方法完成了任务,线程才会返回到Running状态。对处于 ThreadState.Unstarted 状态的线程不能调用 Join。
使用此方法确保线程已终止。如果线程不终止,则调用方将无限期阻塞。如果调用 Join 时该线程已终止,此方法将立即返回。

Imports System.Threading
Public Class JoiningThreadPublic Shared SecondThread As ThreadPublic Shared FirstThread As ThreadShared Sub First() '第一线程For i As Integer = 1 To 50Console.Write(i & " ")NextConsole.WriteLine("First finish!")End SubShared Sub Second() '第二线程FirstThread.Join() '连接到第一线程,只有第一线程终止后,第二线程才能接手运行Console.WriteLine("Second start...")For i As Integer = 51 To 100Console.Write(i & " ")NextEnd SubPublic Shared Sub Main()FirstThread = New Thread(AddressOf First)SecondThread = New Thread(AddressOf Second)FirstThread.Start()SecondThread.Start()Console.ReadLine()End Sub
End Class

五、何时选择线程
     尽管线程耗费内存(保存线程现场、指令代码)和耗费CPU,导致我们尽可能少用线程。但是,线程的确给我们带来了很多方便,
     1.后台运行
         程序中的一些费时功能(如计算、下载等)与UI同处一个线程时,会产生UI界面响应不过来、类似死机的假相,特别是大量的后台数据查询等,此时用线程正好。例:搜索文件。

 Imports System.Threading
Imports System.IO
Public Class Form1Dim searchTerm As StringDim totalFiles As IntegerDim blnSimple As BooleanDelegate Sub ClearText()Delegate Sub AddText(ByVal txt As String)Private Sub btnSimple_Click(sender As Object, e As EventArgs) Handles btnSimple.ClickSearch()blnSimple = TrueEnd SubPublic Sub Search()Invoke(New ClearText(AddressOf ClearList))searchTerm = TextBox1.TexttotalFiles = 0SearchDirectory("F:\Tools")End SubPublic Sub SearchDirectory(ByVal Path As String)Dim di As New DirectoryInfo(Path)Dim f() As FileInfo = di.GetFiles(searchTerm)Dim myFile As FileInfoFor Each myFile In fIf ListBox1.InvokeRequired = True ThenInvoke(New AddText(AddressOf AddList), myFile.FullName)End IfNextDim d() As DirectoryInfo = di.GetDirectories(searchTerm)Dim myDir As DirectoryInfoFor Each myDir In dSearchDirectory(myDir.FullName)NextEnd SubPrivate Sub btnMutli_Click(sender As Object, e As EventArgs) Handles btnMutli.ClickDim t As New Thread(AddressOf Search)blnSimple = Falset.Start()End SubPrivate Sub ClearList()ListBox1.Items.Clear()End SubPrivate Sub AddList(ByVal it As String)ListBox1.Items.Add(it)End Sub
End Class

说明:用SimpleThread按钮运行,明显有停滞出现;但用多线程MultiThread却没任何感觉,很流畅。另外由于其它线程无法直接操作控件:
     如果使用多线程来提高 Windows 窗体应用程序的性能,则必须确保以线程安全方式调用控件。访问 Windows 窗体控件本质上不是线程安全的。 如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。 还可能会出现其他与线程相关的 Bug,例如争用情况和死锁。 确保以线程安全方式访问控件非常重要。
常用的方法来判断控件是否需要委托invoke执行,用Control.InvokeRequired 属性:
     判断当前的线程是不是该窗体所在的线程,如果不是,需要Invoke到窗体线程去。当有一个非当前线程的线程访问的时候自动就为True了,在当前线程中访问一直是False的。
     C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它。此时它将会在内部调用new MethodInvoker(LoadGlobalImage)来完成下面的步骤,这个做法保证了控件的安全,你可以这样理解,有人想找你借钱,他可以直接在你的钱包中拿,这样太不安全,因此必须让别人先要告诉你,你再从自己的钱包把钱拿出来借给别人,这样就安全了

2.外部资源
     在访问外部资源(访问非本地系统上的资源)的时候。这可能是一个数据库进程或者是网络上的一个网络文件共享。网络性能可能对应用程序性能产生不利的影响。当用户界面花费时间取得数据及更新列表框时,用户界面会冻结。我们可以只通过创建一个新线程并在这个线程中执行数据库代码的方式,再次修正这种情况。

六、不适合创建线程
      有些情况创建线程是非常不利的。
      1. 再次访问的执行顺序
         线程不会以你希望的默认顺序执行!
        同样方法代码,尽管被不同的线程先后进行调用,但它们的执行顺序并不确定,先执行的线程并不一定最前,后执行的线程也并不一定最后结果。简言之,线程的执行顺序并不是视觉上确定的先后顺序。

Imports System.Threading
Public Class ThreadOrderShared tl As ThreadShared t2 As ThreadPublic Shared Sub WriteFinished(ByVal threadName As String)Select Case threadNameCase "Tl"Console.WriteLine()Console.WriteLine("Tl Finished")Case "T2"Console.WriteLine()Console.WriteLine("T2 Finished”)End SelectEnd SubPublic Shared Sub Main()tl = New Thread(AddressOf Increment)t2 = New Thread(AddressOf Increment)tl.Name = "Tl"t2.Name = "T2"tl.Start()        '1、t1先执行t2.Start()Console.ReadLine()End SubPublic Shared Sub Increment()For i As Long = 1 To 1000000If i Mod 100000 = 0 ThenConsole.Write("{" + Thread.CurrentThread.Name + "}")End IfNextWriteFinished(Thread.CurrentThread.Name)End Sub
End Class

说明:尽管t1先执行,但并不一定它最先走进CPU。线程由于是操作系统来调配,顺序变得不可控。下面三次运行的结果都不相同,注意谁先显示,谁先结束。
 
     这样看可能有点麻烦,上面代码不变,只是增加一个全局的计数器,把每次结果后接计数器,看一下3次输出结果:顺序与结束的混乱一下就看出来了。
 

2.循环中的线程
    循环中依次产生的线程并不一定依次执行和依次完成,甚至产生阻塞或互锁。

    Public Shared Sub SendAllEmail()For i As Integer = 0 To al.Count - 1Dim t As Thread = LoopingThreads.CreateEmail(AddressOf Mailer.MailMethod, al(i), "johndoe@somewhere.com", "Threading in a loop", "Mail Example")t.Start()t.Join(Timeout.Infinite)NextEnd Sub

上面依次逐一发送邮件,但如果有一个发生不成功,会怎么样?另外看似是队列进入,但能不能到达CPU谁也没法说清,如果皇帝就是cpu,妃子就是线程,若妃子们都打扮好了(线程Start),就等待着皇帝宠幸了。但皇帝决定翻牌宠幸谁呢?皇帝可不管谁先打扮好(Start),他看中那个就宠幸谁先,这个是皇帝决定的。尽管皇家有祖制(线程先后轮询规则),但毕竟皇上主观性太大,无法确定谁先谁后。

一个通用的编程惯例是将工作放到一个队列中,由一个服务处理。例如,—家银行可能将一个基于XML的文件放到一个网络目录中,使得在另一个服务器上运行的服务程序获得这个文件。这个服务将浏览目录,査找新的文件,然后一次处理一个文件。 如果一次不只将一个文件放到目录中,那么服务将依次地处理文件。在一个典型的环境中,新文件将很少被放在这个目录中。基于这个信息,乍一看上去,查找到一个文件的时候好像是启动一个线程的好时机。您可能是对的,但是考虑—下,如果处理这些文件的服务程序停止了那么会发生什么呢。如果一个网络问题在很长时间里阻止服务访问这个目录,那么会发生什么呢?目录中的文件会堆积起来。当服务最后再次启动时,或者运行服务再次访问目录时,每一个文件基本上都会在服务上产生一个新线程。任何使用过这个模型的用户都可以告诉您这种情况可以使服务器停止运行。

如果您的工作被放到一个队列中,并且您觉得应该使用多线程, 您可以考虑使用线程池。

VB.net学习笔记(二十六)线程的坎坷人生相关推荐

  1. JVM 学习笔记二十六、JVM监控及诊断工具-GUI篇

    二十六.JVM监控及诊断工具-GUI篇 1.工具概述 使用上一张命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但他们存在下列局限: (1)无法获取方法级别的分析数据,如方法间的调用关系 ...

  2. Jenkins 持续集成 概念(学习笔记二十六)

    持续集成:提交.测试.构建.测试.部署 前不久接触了持续集成(Continuous Integration,CI). 一.持续集成是什么 首先说说"集成"的概念.在实际的软件开发中 ...

  3. 立创eda学习笔记二十六:手把手教你使用立创eda的官方教程

    可以通过以下办法找到教程: 1,在软件界面点帮助-使用教程 2,在网站首页-帮助-教程进入 如何使用教程: 这里是一级目录,其实对新手最有用的是前面3个部分,后面的仿真先不看. 常见问题里面不光是讲的 ...

  4. [傅里叶变换及其应用学习笔记] 二十六. 高维傅里叶变换的推导

    高维意味着函数中有多个变量,典型的高维傅里叶应用为图像处理. 一个二维图像的亮度(灰度)可以用$f(x_1,x_2)$来表示,以lena为例,图像平面作为$x_1,x_2$平面,灰度作为$z$轴,形成 ...

  5. Java学习笔记二十六:Java多态中的引用类型转换

    Java多态中的引用类型转换 引用类型转换: 1.向上类型转换(隐式/自动类型转换),是小类型到大类型的转换: 2.向下类型转换(强制类型转换),是大类型到小类型的转换: 3.instanceof运算 ...

  6. OpenCV学习笔记(十六)——CamShift研究 OpenCV学习笔记(十七)——运动分析和物体跟踪Video OpenCV学习笔记(十八)——图像的各种变换(cvtColor*+)imgproc

    OpenCV学习笔记(十六)--CamShift研究 CamShitf算法,即Continuously Apative Mean-Shift算法,基本思想就是对视频图像的多帧进行MeanShift运算 ...

  7. python数据挖掘学习笔记】十六.逻辑回归LogisticRegression分析鸢尾花数据

    但是很多时候数据是非线性的,所以这篇文章主要讲述逻辑回归及Sklearn机器学习包中的LogisticRegression算法 #2018-03-28 16:57:56 March Wednesday ...

  8. python分析鸢尾花数据_python数据挖掘学习笔记】十六.逻辑回归LogisticRegression分析鸢尾花数据...

    但是很多时候数据是非线性的,所以这篇文章主要讲述逻辑回归及Sklearn机器学习包中的LogisticRegression算法 #2018-03-28 16:57:56 March Wednesday ...

  9. OpenCV学习笔记(十六):直方图均衡化:equalizeHist()

    OpenCV学习笔记(十六):直方图均匀化:equalizeHist() 参考博客: 直方图均衡化的数学原理 直方图匹配的数学原理 直方图均衡化广泛应用于图像增强中: 直方图均衡化处理的"中 ...

  10. QT学习笔记(十六):setwindowflags的属性总结

    QT学习笔记(十六):setwindowflags的属性总结 此枚举类型用于为小部件指定各种窗口系统属性.它们是不常用的,但在一些情况下是必要的.其中一些标志取决于底层窗口管理器是否支持它们. 主要类 ...

最新文章

  1. centos7.x 升级svn版本到指定版本(1.10)
  2. linux各种压缩包使用方法
  3. 虚拟机下ubuntu10.04挂载NFS
  4. vue-webpack项目本地开发环境设置代理解决跨域问题
  5. 用.net中的SqlBulkCopy类批量复制数据 (转载)
  6. Navicat Win 和 Mac 视图类快捷键对比
  7. hdu4318 最短路变形
  8. Java修饰符public,private,protected及默认的区别
  9. Linux下编译protobuf
  10. 蓝桥杯 算法训练 数的潜能 正整数分解使得乘积最大问题
  11. meethigher-腾讯课堂自动签到
  12. 计算机专业考注册测绘师经验,注册测绘师考试攻略
  13. 交管123缴费显示代理服务器异常,交管12123服务异常怎么回事?交管12123网络请求失败怎么办...
  14. C++ 拓扑排序(AOV网络)
  15. Hibernate第七篇【对象状态、一级缓存】
  16. C++批量修改文件名字
  17. ipad已停用恢复系统 2种方法
  18. php星期代码,PHP星期几获取代码
  19. 老子《道德经》第六十二章
  20. 3Par 8000存储的一些命令

热门文章

  1. GetDlgItem(函数详解)
  2. CMMI-组织级过程关注
  3. currentStyle与getComputedStyle应用
  4. 朱民:Fintech“逼迫”金融机构剥离内生封闭产业链,机构监管走向功能监管
  5. VS2008卸载导致的Office不能正常使用
  6. 前端网络基础-网络分层模型
  7. 计算机主硬盘隐藏分区大小,详细教您win10硬盘怎么隐藏分区
  8. VC2010无法启动程序,系统找不到指定文件
  9. 热噪声:nv/√Hz
  10. 课表 php源码,php+excel通用课表查询系统 v2.2