VB.net学习笔记二十六线程的坎坷人生



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

线程的生存期如下:


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

Imports System.Threading
Public Class ThreadSleep
    Public Shared worker As Thread
    Public Shared worker2 As Thread
    Public Shared Sub Main()
        Console.WriteLine("Entering the Sub Main!")
        worker = New Thread(AddressOf Counter)
        worker2 = New Thread(AddressOf Counter2)
        worker2.Priority = ThreadPriority.Highest
        worker.Start()
        worker2.Start()
        Console.WriteLine("Exiting the Sub Main!")
        Console.ReadLine()
    End Sub
    Public Shared Sub Counter()
        Console.WriteLine("Entering Counter")
        For i As Integer = 1 To 50
            Console.Write(i & " ")
            If i = 10 Then
                Console.WriteLine(" worker_线程暂停1秒...")
                worker.Sleep(1000) '等效于worker.Sleep(TimeSpan.FromSeconds(1))
                Console.WriteLine("worker线程恢复:")
            End If
        Next
        Console.WriteLine()
        Console.WriteLine("Exiting Counter")
    End Sub
    Public Shared Sub Counter2()
        Console.WriteLine("Entering Countcr2")
        For i As Integer = 51 To 100
            Console.Write(i & " ")
            If i = 70 Then
                Console.WriteLine(" worker2 线程暂停5秒...")
                worker2.Sleep(5000)
                Console.WriteLine("worker2线程恢复:")
            End If
        Next
        Console. 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 Interrupt
    Public Shared sleeper As Thread
    Public Shared worker As Thread
    Public 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 Sub
    Public Shared Sub SleepingThread()
        For i As Integer = 1 To 50
            Console.Write(i & " ")
            If i = 10 Or i = 20 Or i = 30 Then
                Console.WriteLine("Going to sleep at: " & i)
                Try  'ThreadInterruptedException  在中断的线程中引发,但要在该线程阻塞之后才引发。 
                    sleeper.Sleep(30) '这里易抛异常,用Try
                Catch ex As Exception '原因见4.2(原因http://blog.csdn.net/wguoyong/article/details/50918322)
                End Try
            End If
        Next
    End Sub
    Public Shared Sub AwakeTheThread()
        Dim i As Integer
        For i = 51 To 100
            Console.Write(i & " ")
            If sleeper.ThreadState = System.Threading.ThreadState.WaitSleepJoin Then
                Console.WriteLine("Interrupting the sleeping thread")
                sleeper.Interrupt()
            End If
        Next
    End 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 JoiningThread
    Public Shared SecondThread As Thread
    Public Shared FirstThread As Thread
    Shared Sub First() '第一线程
        For i As Integer = 1 To 50
            Console.Write(i & " ")
        Next
        Console.WriteLine("First finish!")
    End Sub
    Shared Sub Second() '第二线程
        FirstThread.Join() '连接到第一线程,只有第一线程终止后,第二线程才能接手运行
        Console.WriteLine("Second start...")
        For i As Integer = 51 To 100
            Console.Write(i & " ")
        Next
    End Sub
    Public 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 Form1
    Dim searchTerm As String
    Dim totalFiles As Integer
    Dim blnSimple As Boolean
    Delegate Sub ClearText()
    Delegate Sub AddText(ByVal txt As String)
    Private Sub btnSimple_Click(sender As Object,e As EventArgs) Handles btnSimple.Click
        Search()
        blnSimple = True
    End Sub
    Public Sub Search()
        Invoke(New ClearText(AddressOf ClearList))
        searchTerm = TextBox1.Text
        totalFiles = 0
        SearchDirectory("F:\Tools")
    End Sub
    Public Sub SearchDirectory(ByVal Path As String)
        Dim di As New DirectoryInfo(Path)
        Dim f() As FileInfo = di.GetFiles(searchTerm)
        Dim myFile As FileInfo
        For Each myFile In f
            If ListBox1.InvokeRequired = True Then
                Invoke(New AddText(AddressOf AddList),myFile.FullName)
            End If
        Next
        Dim d() As DirectoryInfo = di.GetDirectories(searchTerm)
        Dim myDir As DirectoryInfo
        For Each myDir In d
            SearchDirectory(myDir.FullName)
        Next
    End Sub
    Private Sub btnMutli_Click(sender As Object,e As EventArgs) Handles btnMutli.Click
        Dim t As New Thread(AddressOf Search)
        blnSimple = False
        t.Start()
    End Sub
    Private Sub ClearList()
        ListBox1.Items.Clear()
    End Sub
    Private 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 ThreadOrder
    Shared tl As Thread
    Shared t2 As Thread
    Public Shared Sub WriteFinished(ByVal threadName As String)
        Select Case threadName
            Case "Tl"
                Console.WriteLine()
                Console.WriteLine("Tl Finished")
            Case "T2"
                Console.WriteLine()
                Console.WriteLine("T2 Finished”)
        End Select
    End Sub
    Public 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 Sub
    Public Shared Sub Increment()
        For i As Long = 1 To 1000000
            If i Mod 100000 = 0 Then
                Console.Write("{" + Thread.CurrentThread.Name + "}")
            End If
        Next
        WriteFinished(Thread.CurrentThread.Name)
    End Sub
End Class
说明:尽管t1先执行,但并不一定它最先走进CPU。线程由于是操作系统来调配,顺序变得不可控。下面三次运行的结果都不相同,注意谁先显示,谁先结束。

这样看可能有点麻烦,上面代码不变,只是增加一个全局的计数器,把每次结果后接计数器,看一下3次输出结果:顺序与结束的混乱一下就看出来了。


2.循环中的线程
循环中依次产生的线程并不一定依次执行和依次完成,甚至产生阻塞或互锁。
    Public Shared Sub SendAllEmail()
        For i As Integer = 0 To al.Count - 1
            Dim 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)
        Next
    End Sub
上面依次逐一发送邮件,但如果有一个发生不成功,会怎么样?另外看似是队列进入,但能不能到达CPU谁也没法说清,如果皇帝就是cpu,妃子就是线程,若妃子们都打扮好了(线程Start),就等待着皇帝宠幸了。但皇帝决定翻牌宠幸谁呢?皇帝可不管谁先打扮好(Start),他看中那个就宠幸谁先,这个是皇帝决定的。尽管皇家有祖制(线程先后轮询规则),但毕竟皇上主观性太大,无法确定谁先谁后。

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Format[$] ( expr [ , fmt ] ) format 返回变体型 format$ 强制返回为文本 -------------------------------- 数字类型的格式化 --------------------------------     固定格式参数:     General Number 普通数字,如可以用来去掉千位分隔号     format$("100,1
VB6或者ASP 格式化时间为 MM/dd/yyyy 格式,竟然没有好的办法, Format 或者FormatDateTime 竟然结果和系统设置的区域语言的日期和时间格式相关。意思是尽管你用诸如 Format(Now, "MM/dd/yyyy"),如果系统的设置格式区域语言的日期和时间格式分隔符是"-",那他还会显示为 MM-dd-yyyy     只有拼凑: <%response.write
在项目中添加如下代码:新建窗口来显示异常信息。 Namespace My ‘全局错误处理,新的解决方案直接添加本ApplicationEvents.vb 到工程即可 ‘添加后还需要一个From用来显示错误。如果到这步还不会则需要先打好基础啦 ‘======================================================== ‘以下事件
转了这一篇文章,原来一直想用C#做k3的插件开发,vb没有C#用的爽呀,这篇文章写与2011年,看来我以前没有认真去找这个方法呀。 https://blog.csdn.net/chzjxgd/article/details/6176325 金蝶K3 BOS的插件官方是用VB6编写的,如果  能用.Net下的语言工具开发BOS插件是一件很愉快的事情,其中缘由不言而喻,而本文则是个人首创,实现在了用V
Sub 分列() ‘以空格为分隔符,连续空格只算1个。对所选中的单元格进行处理 Dim m As Range, tmpStr As String, s As String Dim x As Integer, y As Integer, subStr As String If MsgBox("确定要分列处理吗?请确定分列的数据会覆盖它后面的单元格!", _
  窗体代码 1 Private Sub Text1_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single) 2 Dim path As String, hash As String 3 For Each fil
  Imports MySql.Data.MySqlClient Public Class Form1 ‘ GLOBAL DECLARATIONS Dim conString As String = "Server=localhost;Database=net2;Uid=root;Pwd=123456;" Dim con As New MySqlConnection
‘導入命名空間 Imports ADODB Imports Microsoft.Office.Interop   Private Sub A1() Dim Sql As String Dim Cnn As New ADODB.Connection Dim Rs As New ADODB.Recordset Dim S As String   S = "Provider=OraOLEDB.Oracl
Imports System.IO Imports System.Threading Imports System.Diagnostics Public Class Form1 Dim A(254) As String    Function ping(ByVal IP As Integer) As String Dim IPAddress As String IPAddress = "10.0.
VB运行EXE程序,并等待其运行结束 参考:https://blog.csdn.net/useway/article/details/5494084 Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long Pr
今天碰到一个问题,登陆的时候,如果不需要验证手机号为空,则不去验证手机号 因为登陆的时候所有的验证信息都存放在一个数组里 Dim CheckUserInfo() As String ={UserBirthday, SecEmail, UserMob, UserSex, RealNameFirst, RealName, CheckCardID, CheckCardType, Contactemail
在VB6.0中,数据访问接口有三种: 1、ActiveX数据对象(ADO) 2、远程数据对象(RDO) 3、数据访问对象(DAO) 1.使用ADO(ActiveX Data Objec,ActiveX数据对象)连接SQL Server 1)使用ADO控件连接 使用ADO控件的ConnectionString属性就可以连接SQL Server,该属性包含一个由分号分隔的argument=value语
注:大家如果没有VB6.0的安装文件,可自行百度一下下载,一般文件大小在200M左右的均为完整版的软件,可以使用。   特别提示:安装此软件的时候最好退出360杀毒软件(包括360安全卫士,电脑管家等,如果电脑上有这些软件的话),因为现如今的360杀毒软件直接会对VB6.0软件误报,这样的话就可能会在安装过程中被误报阻止而导致安装失败,或者是安装后缺乏很多必须的组件(其它的杀毒软件或安全卫士之类的
Private Sub Form_Load() Call conndb End Sub Private Function conndb() Dim cn As New ADODB.Connection Dim rs As New ADODB.Recordset Dim strCn, sql As String Dim db_host As String Dim db_user As String
  PPSM06S70:  Add  moddate  EDITSPRINTJOB:  MAX(TO_CHAR(ETRN.MODDATE, ‘yyyy/mm/dd/HH24:MI AM‘)) ACTUAL_SHIPDATE   4.Test Scenario (1) :Query SQL Test DN:8016578337 SELECT CTRN.TKCTID TRUCK_ID,        
  沒有出現CrystalReportViewer時,須安裝CRforVS_13_0. 新增1個數據集,新增1個數據表,添加二列,列名要和資料庫名一樣. 修改目標Framework 修改app.config, <startup >改成<startup useLegacyV2RuntimeActivationPolicy ="true">  CrystalReport1.rpt增加數據庫專家 在表單
Imports System.Threading Imports System Public Class Form1 Dim th1, th2 As Thread Public Sub Method1() Dim i As Integer For i = 1 To 100 If Me.Label1.BackColor =
Friend Const PROCESS_ALL_ACCESS = &H1F0FFF = 2035711 Friend Const PROCESS_VM_READ = &H10 Friend Const PROCESS_VM_WRITE = &H20 Friend Const PAGE_READONLY = &H2 Friend Const PAGE_READWRITE = &H4 Friend
以下代码随手写的 并没有大量测试 效率也有待提升 如果需要C#的请自行转换 Function SplitBytes(Data As Byte(), Delimiter As Byte()) As List(Of Byte()) Dim i = 0 Dim List As New List(Of Byte()) Dim bytes As New
Imports System.Data.SqlClient Public Class Form1 REM Public conn1 As SqlConnection = New SqlConnection("server=.; Integrated Security=False;Initial Catalog= mydatabase1; User ID= sa;password")