重要的Thread类在System.Threading中。System.Threading 命名空间提供类和接口,使多线程的编程。 除了用于同步线程活动和访问数据的类 (Mutex, ,Monitor, ,Interlocked, ,AutoResetEvent, ,依此类推),此命名空间包括 ThreadPool 类,它允许您使用的系统提供线程池和 Timer 在线程池线程执行的回调方法的类。
1.Thread 类
Thread类创建和控制线程,设置其优先级并获取其状态。
进程启动时,公共语言运行时将自动创建一个前台线程执行应用程序代码。 此主前台线程,以及一个进程可以创建一个或多个线程以执行与该进程关联的程序代码的一部分。 在前台或后台中,可以执行这些线程。 此外,您可以使用 ThreadPool 类在由公共语言运行时管理的工作线程上执行代码。
启动线程 通过提供一个委托,表示该线程是在其类构造函数中执行的方法来启动一个线程。 然后,可以调用 Start 方法开始执行。Thread 构造函数可以采用两个委托类型,具体取决于是否可以将参数传递给要执行的方法.
线程对象的检索问题您可以使用静态 (Shared 在 Visual Basic 中) CurrentThread 属性来检索对当前执行线程的线程正在执行的代码中的引用。
前台和后台线程实例 Thread 类表示前台线程或后台线程。 后台线程将与前台线程有一处例外 ︰ 如果所有前台线程已都终止,后台线程不会保留正运行的进程。 一旦所有前台线程已都停止,则运行时将停止所有后台线程,并关闭。
区域性和线程每个线程都有一个区域性,由表示 CurrentCulture 属性和 UI 区域性,由表示 CurrentUICulture 属性。 当前区域性支持分析和格式设置,字符串比较和排序,诸如区分区域性的操作,还可以控制书写系统和线程使用的日历。 当前 UI 区域性提供用于区分区域性检索资源文件中的资源。
实例化一个新线程时,其区域性和 UI 区域性定义的当前系统区域性和 UI 区域性,而不是区域性和 UI 区域性从中创建新线程的线程。 这意味着,例如,如果当前系统区域性为英语 (美国),主要应用程序线程的当前区域性为法语 (法国),通过调用创建一个新线程的区域性 Thread(ParameterizedThreadStart) 从主线程的构造函数是英语 (美国) 和不是法语 (法国)。
2.线程总览
Thread的方法:
Thread的属性
3.创建线程
用New Thread( )创建一个新的线程,再用Start执行。
线程ID:ID是创建线程时操作系统自动生成的,用来标记线程的(线程编号),由操作系统来管理且不得重复(即线程ID是唯一的),此ID只在线程的生存期内有效,线程消失则该ID撤消。
线程Handle:为一个32位指针。OS有一张维护HANDLE的表单,里面HANDLE的引用计数和有关的属性,尽管相同的句柄一定标识同一线程,但同一线程可能拥有两个打开的句柄,因此,不能用句柄来区分两个线程是否是同一线程。
- Imports System.Threading
- Public Class SimpleThread
- Public Shared Sub main()
- Dim obj As New SimpleThread
- obj.SimpleMethod() '主线程执行
- '-----------
- 'Dim t As New Thread(AddressOf obj.SimpleMethod) '1、ThreadStar上委托调用实例的方法
- Dim ts As New ThreadStart(AddressOf obj.SimpleMethod) '2.同1先指定执行方法,再创建、执行
- Dim t As New Thread(ts)
- '----------
- t.Start() '线程执行
- Console.Read()
- End Sub
- Public Sub SimpleMethod()
- Dim i As Integer = 5
- Dim x As Integer = 10
- Dim iResult As Integer = i * x
- Console.WriteLine("Result is " & iResult.ToString & " from thread ID:" &
- Thread.CurrentThread.ManagedThreadId.ToString) '显示托管线程ID
- End Sub
- End Class
例:再看一下新建多个线程时,注意一下ID编号和ID出现的顺序。(运行程序时,本机开了360浏览器、word、OCR软件、优酷PC端、QQ等多个程序)
- Imports System.Threading
- Public Class SimpleThread
- Public Shared Sub main()
- Dim i As Integer
- For i = 1 To 10
- Dim t As New Thread(AddressOf SimpleMethod)
- t.Start()
- Next
- Console.Read()
- End Sub
- Public Shared Sub SimpleMethod()
- Console.WriteLine("Worker Thread: " & Thread.CurrentThread.ManagedThreadId.ToString)
- End Sub
- End Class
4.ThreadStart 委托
表示在 Thread 上执行的方法。Visual Basic 用户在创建线程时可以省略 ThreadStart 构造函数。在传递所用方法时使用 AddressOf 运算符,例如 Dim t As New Thread(AddressOf ThreadProc)。Visual Basic 自动调用 ThreadStart 构造函数。
简言之:书写代码:Dim t As New Thread(AddressOf ThreadProc),但VB.net内部自动完成的是下面代码:
Dim t As New Thread(New ThreadStart(AddressOf ThreadProc))
ThreadStart的优势主要体现在分支选择执行上。
例:分支选择决定在新的线程上决定执行哪个方法.(1处标注多个委托方法,2处选择哪个,这时并没创建线程实例。只有到了3处才开始构造,然后4处执行)
- Imports System.Threading
- Public Class ThreadStartBranding
- Enum UserClass '枚举
- ClassAdmin
- ClassUser
- End Enum
- Shared Sub main()
- ExecuteFor(UserClass.ClassAdmin)
- ExecuteFor(UserClass.ClassUser)
- Console.Read()
- End Sub
- Shared Sub ExecuteFor(ByVal uc As UserClass)
- Dim ts As ThreadStart
- Dim tsAdmin As New ThreadStart(AddressOf AdminMethod) '1.ThreadStart 委托
- Dim tsUser As New ThreadStart(AddressOf UserMethod)
- If uc = UserClass.ClassUser Then '2.选择决定哪个委托方法
- ts = tsUser
- Else
- ts = tsAdmin
- End If
- Dim t As New Thread(ts) '3.构造线程
- t.Start() '4.执行
- End Sub
- Shared Sub AdminMethod()
- Console.WriteLine("Admin Method")
- End Sub
- Shared Sub UserMethod()
- Console.WriteLine("User Method")
- End Sub
- End Class
5.线程的状态
线程有多个状态值,创建的线程最初处于Unstarted状态,通过Start方法后线程转为Running状态,中间可以处于睡眠Sleep或挂起Suspend状态,中途还有请求状态:StopRequested(请求停止)、SuspendRequested(请求挂起)、AbortRequested(请求中止),当代码执行完成后自动停止Stopped状态,也可手动或意外中止Aborted状态。线程可以同时处于多个组合状态,比如,请求中止时,线程处于Running与AbortRequested状态。
线程与另一线程是各行其是的运行,一个线程睡眠并不影响另一个线程的运行。线程里可创建另一个线程,原线程称父线程,创建的线程称子线程。前台线程全部终结后,后台线程即使没结束也会尤如无根之萍,也会随之终结。默认新创建的线程为前台线程。线程停止并不是线程退出
例:主线程中创建一新线程,主线程睡眠时(1处),新建的线程状态不受影响仍然运行(2处),委托方法执行完成后线程停止运行(3处)但并不是终结(释放资源)。主线程和新建的线程都是前台线程(4处).
- Imports System.Threading
- Public Class ThreadState
- Shared Sub main()
- Dim t As New Thread(AddressOf WorkerFunction)
- t.Start()
- '4、是否后台线程
- Console.WriteLine("Backgroud is " & Thread.CurrentThread.IsBackground & " for main thread.")
- Console.WriteLine("Backgroud is " & t.CurrentThread.IsBackground & " for new thread.")
- '-------
- While t.IsAlive '主线程每睡眠200毫秒后,时钟中断激活主线程(线程启动后未停止时为真)
- Console.WriteLine("Waiting,main thread is back to sleep.")
- Thread.CurrentThread.Sleep(200) '1、主线程睡眠
- End While
- '-------
- Console.WriteLine("Complete,new thread state is " & t.ThreadState.ToString) '3、新线程最后状态
- Console.Read()
- End Sub
- Shared Sub WorkerFunction() '模拟功能,每隔5000毫秒显示新建线程状态
- For i As Integer = 1 To 50000
- If i Mod 5000 = 0 Then
- '2、新建线程的状态(这里的当前线程指的是主线程中创建的新线程)
- Console.WriteLine("Worker: " & Thread.CurrentThread.ThreadState.ToString)
- End If
- Next
- Console.WriteLine("WorkerFunction is completed")
- End Sub
- End Class
6.线程的优先级
线程的优先级决定各个线程之间相对的优先级。ThreadPriority枚举定义可用于设置线程优先级的值,可用的值是:Highest(最高值)、AboveNormal(高于正常值}、Normal(正常值)、BelowNormaK低于正常值)、Lowest(最低值)。在运行时内创建的线程最初被分配 Normal 优先级,而在运行时外创建的线程在进入运行时时将保留其先前的优先级。 可以通过访问线程的 Priority 属性来获取和设置其优先级。
线程的优先级不影响该线程的状态;在操作系统可以调度该线程之前,该线程的状态必须为 Running。
例:创建并运行两线程,优先级高的线程优先执行。
- Imports System.Threading
- Public Class ThreadPriority
- Public Shared worker1 As Thread
- Public Shared worker2 As Thread
- Public Shared Sub main()
- Console.WriteLine("Entering Sub Main()")
- worker1 = New Thread(AddressOf TestPriority1)
- worker2 = New Thread(AddressOf TestPriority2)
- worker1.Name = "TestPriority1()_Thread"
- worker2.Name = "TestPriority2()_Thread"
- worker2.Priority = Threading.ThreadPriority.Highest
-
- worker1.Start()
- worker2.Start()
- Console.ReadLine()
- End Sub
- Public Shared Sub TestPriority1()
- Console.WriteLine("-------------------")
- Console.WriteLine("Name:" & worker1.Name)
- Console.WriteLine("State:" & worker1.ThreadState.ToString)
- Console.WriteLine("Priority:" & worker1.Priority.ToString)
- End Sub
- Public Shared Sub TestPriority2()
- Console.WriteLine("===================")
- Console.WriteLine("Name:" & worker2.Name)
- Console.WriteLine("State:" & worker2.ThreadState.ToString)
- Console.WriteLine("Priority:" & worker2.Priority.ToString)
- End Sub
- End Class
7.计时器
由于线程和其余的应用程序代码不按次序运行, 所以无法确定读写共享资源的先后次序。简单的方法是使用计时器:按特定正则间隔执行一个方法,检查在继续下面的操作之前所要求的动作已经完成。
计时器由两个对象组成,一个是TimerCallback对象,另一个是Timer对象。TimerCallback对象定义在指定间隔执行的动作,而Timer对象本身就是计时器。TimerCallback将一个特定的方法与计时器联系起来, Timer的构造函数(由重载得到)需要4个变量。
Timer(TimerCallback, Object, Int64, Int64)或Timer(TimerCallback, Object, TimeSpan, TimeSpan)
第一个参数是TimerCallback对象,第二个是用来将状态传输给指定的方法的一个对象。后两个参数是在此之后开始计时的周期,以及触发TimerCallback方法调用使用的周期。可以是整数或者长整型或System.TimeSpan对象。
dueTime 在 callback 参数调用其方法之前延迟的时间量。指定 -1 毫秒以防止启动计时器。指定零 (0) 可立即启动计时器。
Period 在调用 callback 所引用的方法之间的时间间隔。指定 -1 毫秒可以禁用定期终止。
例:用计时器(tmr)探测新线程(t)访问共享资源(message)的情况。先新建一个生产文字的线程(1处)并运行,再创建计时器(3处)来探测线程中文本(message)生成情况。计时器每5毫秒触发一次进行查看,无信息时(4处)将显示提示,有信息时就输出并退出计时器(5处),最后6处重置标志使7处循环退出。由于程序运行很快,为了模拟计算费时,在2处浪费10毫秒以达模拟效果。
- Imports System.Threading
- Imports System.Text
- Public Class ThreadTimer
- Private Shared message As String
- Private Shared tmr As Timer
- Private Shared complete As Boolean
- Public Shared Sub main()
- Dim obj As New ThreadTimer
- Dim t As New Thread(AddressOf obj.GenerateText)
- t.Start() '1、启动产生文本的线程
-
-
- Dim tmrCallBack As New TimerCallback(AddressOf obj.GetText)
- '--------------------------------------- 3、每5毫秒触发立即启动方法
- tmr = New Timer(tmrCallBack, Nothing, TimeSpan.Zero, TimeSpan.FromMilliseconds(5))
- Do
- If complete Then Exit Do '7、退出
- Loop
- Console.WriteLine("Exiting Main...")
- Console.ReadLine()
- End Sub
- Public Sub GenerateText() '产生字符并接入末尾
- Dim i As Integer
- Dim sb As StringBuilder = New StringBuilder
- For i = 1 To 10
- Thread.Sleep(10) '2、模拟计算耗时
- sb.Insert(sb.Length, " This is line ")
- sb.Insert(sb.Length, i.ToString)
- sb.Insert(sb.Length, vbCrLf)
- Next
- message = sb.ToString
- End Sub
- Public Sub GetText() '非空时输出字符
- If message Is Nothing Then '4、无信息时,提示
- Console.WriteLine(Now.Second.ToString & ":" & Now.Millisecond.ToString &
- " Not message,waiting...")
- Exit Sub
- End If
- Console.WriteLine("Message is :-------------------------------")
- Console.WriteLine(message)
- tmr.Dispose() '5、计时器释放,失效
- complete = True '6、主程序循环标志退出
- End Sub
- End Class
注意:
当2处循环次数改为100次时,会发现message会输出两次(尽管代码只有一次),因为是5毫秒触发一次,当一次输出时(100行)时超过5毫秒,这第二次5毫秒后的触发又来了,所以有两次情况。当循环次数改为200时,会有三次message的输出,总之次数越长就会越混乱,最好的办法就是把计时器释放提到message之前,还没有输出就让其失效
8.线程生线程
一个线程可产生出多个线程,每个方法都可在单独的线程时间片中执行。例,先新添一类
- Imports System.Threading
- Public Class Car
- Public Sub StartTheEngine()
- Console.WriteLine("Starting the engine!")
- Dim batt As Thread = New Thread(AddressOf CheckTheBattery)
- Dim fuel As Thread = New Thread(AddressOf CheckForFuel)
- Dim eng As Thread = New Thread(AddressOf CheckTheEngine)
- batt.Start()
- fuel.Start()
- eng.Start()
- UseTime()
- Console.WriteLine("Engine is ready!")
- End Sub
- Private Sub CheckTheBattery()
- Console.WriteLine("Checking the Battery!")
- UseTime()
- Console.WriteLine("Finished checking the Battery!")
- End Sub
- Private Sub CheckForFuel()
- Console.WriteLine("Checking for Fuel!")
- UseTime()
- Console.WriteLine("Fuel is available!")
- End Sub
- Private Sub CheckTheEngine()
- Console.WriteLine("Checking the engine!")
- UseTime()
- Console.WriteLine("Finished checking the engine!")
- End Sub
- Private Sub UseTime()
- For i As Integer = 1 To 100000000
- Next
- End Sub
- End Class
再主模块中如下(右为模式图):
执行结果如下:
如果还想再分裂生产线程,将上面Car类CheckTheEngine()方法改为:
- Private Sub CheckTheEngine()
- Dim cke As New Engine
- cke.CheckTheEngine()
- End Sub
并新加类Engine:
- Imports System.Threading
- Public Class Engine
- Public Sub CheckTheEngine()
- Dim chk1 As Thread = New Thread(AddressOf Check1)
- Dim chk2 As Thread = New Thread(AddressOf Check2)
- chk1.Start()
- chk2.Start()
- Console.WriteLine("Checking the engine!")
- For count As Integer = 1 To 100000000
- Next
- Console.WriteLine("Finished checking the engine!")
- End Sub
-
- Private Sub Check1()
- Console.WriteLine("Starting the engine check!!")
- For i As Integer = 1 To 100000000
- Next
- Console.WriteLine("Finished engine check 1 !")
- End Sub
- Private Sub Check2()
- Console.WriteLine("Starting the engine check2!")
- For i As Integer = 1 To 100000000
- Next
- Console.WriteLine("Finished engine check2!")
- End Sub
- End Class
运行的结果:
注意:创建的线程越多,系统需要保持线程的现场和CPU指令的工作就越多。必然对性能产生影响。
|