.NET 全面包含多线程

ZDNet软件频道 时间:2001-10-15 作者:Lamont Adams |  我要评论()
本文关键词:
微软.NET Framework的许多新花样中的一个就是它包含了一个容易而标准的创建自由线程应用程序的方法。

认识Thread对象的优先级
对于CPU时间线程有不同的优行级,这非常像应用程序获得CPU时间片一样。一个线程的Priority(优先级)属性控制它宿主进程时间的多大一个时间片被分配给它。这些优先级,从最大的到最小的排列顺序是:

  • Highest
  • AboveNormal
  • Normal
  • BelowNormal
  • Lowest

记住这些设置控制了有限的进程CPU时间分配给线程的比例。因此,一个拥有最高级别优先级的线程可能不会留下多少时间给其它的线程完成它们的工作。一般来说,控制UI的线程应该给与正常的优先级,而那么没有UI元素的线程可以有一个更低的优先级,以便程序的UI仍然能够对用户做出响应。

控制线程行为
一旦你的新线程已经作好准备并开始运行,你可以通过它实例变量的方法对它实施一些控制。要知道你的线程现在在做些什么,你可以检查它的ThreadState属性。ThreadState返回一个枚举类型,它描述了线程的当前状态。

Sleep方法使一个线程“睡”上一段你指定的时间。在睡眠的时候,一个线程不会消耗CPU周期,所以睡眠是有效的使一个线程等待计算机资源来完成工作的方法。在睡眠的时候,线程的ThreadState属性会返回WaitSleepJoin.

如果你的线程睡了足够长的时间,你可以用Interrupt方法叫醒它。在一个线程在调用Interrupt方法会在这些线程运行的代码里抛出一个ThreadInterruptedException 异常。所以如果你的线程需要知道它是什么时候睡着的,只要设置Catch来捕捉这个异常就行了。

Interrupt 是一个非常违反直观的名字,因为你不能中断一个线程除非它一直是睡着的。如果在被中断的时候线程是醒着的,那么这个中断会被排队,直到下一次线程开始睡眠的时候,这时线程又被马上叫醒。这也许是你不想要的,所以在中断它以前先验证一个线程是不是处于WaitSleepJoin状态。

你还可以中断一个线程并在以后恢复它的运行。Suspend 方法挂起一个线程直到你调用Resume 方法。一个线程可能不会马上挂起它自己,在它真正挂起以前,它的ThreadState属性会是SuspendRequested.一旦它已经被挂起了,ThreadState属性就变成Suspended.那么Suspend/Resume 和 Sleep/Interrupt之间有什么不同呢?两个最大的不同在于:

  • 一个Sleep请求是马上发生的,而一个Suspend请求却被排队,直到下一次Windows将CPU时间分配给另一个线程或者进程的时候。
  • 线程是通过一个异常来知道Interrupt请求的,而一个挂起后重新运行的线程却不知道它已经被挂起并重新开始运行了。

要立即停止并除去一个线程,你应该调用Abort方法。Abort通过在它的代理里抛出一个ThreadAbortException异常来杀死线程。这是不可捕获的异常,但是如果它是在一个Try...Finally代码块里抛出的,那么Finally代码会在线程结束以前执行。


没有了Stop 方法

你可能看到有些书,文章,和文档说一种叫做Stop的方法是结束一个线程的另一方法。但这个方法已经在.Net Framework 的Beta 2版里被删除了。

下面是一个值得特别注意的方法。Join方法会挂起当前执行的线程直到引用的线程结束。例如,给出下面的C#代码:

//为Class1.DoSomething创建代理
ThreadStart delegate;
delegate = new ThreadStart(Class1.DoSomething);
Thread t = new Thread(del); //create new thread
t.Start();
t.Join();
Console.WriteLine("Hello There");

对Console.Writeline 的调用不会执行除非t已经完成了Class1.DoSomething的运行。你在一个线程不能在另一个线程完成工作以前完成自己的工作的时候使用Join方法,或者你在等待所有的线程结束运行以便你能够结束你的应用程序的时候使用它。一个等待Join其它线程的线程,它的ThreadState属性值是WaitSleepJoin.


那么应该在什么时候使用这些东西呢?
一个证明多线程是有用的很好的例子是当你运行一个很耗时的方法的时候,用户可能想要在它完成以前取消它。在只有一个线程的时候要取消它是困难的,但是只要再用一个线程,那么保持UI能够响应就是小菜一碟了。请看下面VB.NET代码的例子。

'Copyright 2001, TechRepublic
'Author: Lamont Adams
'Demonstrates basic free-threading techniques using
'.NET's thread object by creating a cancel dialog
'that can be used to cancel a simulated long-running
'operation on another thread.
'DISCLAIMER:
'Although every effort has been made to ensure that
'the code is safe and will run as intended,
'TechRepublic and CNet Networks cannot be held
'responsible for any damages resulting from
'the execution of this code. Use at your own risk.

Imports System.Threading
Public Class Form1
    'The "main" form
    Inherits System.Windows.Forms.Form
            #Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call


    End Sub

'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
    If disposing Then
        If Not (components Is Nothing) Then
            components.Dispose()
        End If
    End If
    MyBase.Dispose(disposing)
End Sub

Friend WithEvents ListBox1 As System.Windows.Forms.ListBox
Friend WithEvents Button6 As System.Windows.Forms.Button

'Required by the Windows Form Designer
Private components As System.ComponentModel.Container

'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    Me.ListBox1 = New System.Windows.Forms.ListBox()
    Me.Button6 = New System.Windows.Forms.Button()
    Me.SuspendLayout()
    '
    'ListBox1
    '
    Me.ListBox1.Location = New System.Drawing.Point(8, 8)
    Me.ListBox1.Name = "ListBox1"
    Me.ListBox1.Size = New System.Drawing.Size(208, 121)
    Me.ListBox1.TabIndex = 1
    '
    'Button6
    '
    Me.Button6.Location = New System.Drawing.Point(8, 136)
    Me.Button6.Name = "Button6"
    Me.Button6.Size = New System.Drawing.Size(200, 32)
    Me.Button6.TabIndex = 6
    Me.Button6.Text = "start long operation"
    '
    'Form1
    '
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
    Me.ClientSize = New System.Drawing.Size(224, 173)
    Me.Controls.AddRange(New System.Windows.Forms.Control() )
    Me.Name = "Form1"
    Me.Text = "Form1"
    Me.ResumeLayout(False)

End Sub

#End Region

Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button6.Click
    Dim t As Thread

    'Create a new thread that does some processing
    t = New Thread(AddressOf Me.Count)
    'Hand off that thread to the cancel dialog
    Dim f As New Form2(t)
    'make this a background thread so it doesn't interfere with the UI
    t.Priority = ThreadPriority.BelowNormal
    'start the thread
    t.Start()
    'show the cancel dialog
    f.ShowDialog()
    'wait for the other thread to end.
    t.Join()
End Sub

Private Sub Count()
    'This routine will run in a tight loop, and
    'will never end to simulate a long, processor
    'intensive task that might need to be cancelled.
    'By running it on another thread, we can keep
    'the UI responsive.
    Dim i As Long
    Try
        While True
            i = i + 1
            SyncLock ListBox1
                Me.ListBox1.Items.Insert(0, CStr(i))
            End SyncLock
            If i > 320000 Then
                i = 0
            End If
        End While
    Catch
        'pass
    End Try
End Sub
End Class

Public Class Form2
    'The cancellation dialog.
    Inherits System.Windows.Forms.Form
    Private mt As System.Threading.Thread

#Region " Windows Form Designer generated code "

    Public Sub New(ByVal t As System.Threading.Thread)
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call
        mt = t
    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If

        MyBase.Dispose(disposing)
    End Sub

    Friend WithEvents btnCancel As System.Windows.Forms.Button

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.Container

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.btnCancel = New System.Windows.Forms.Button()
        Me.SuspendLayout()
        '
        'btnCancel
        '
        Me.btnCancel.Location = New System.Drawing.Point(48, 24)
        Me.btnCancel.Name = "btnCancel"
        Me.btnCancel.Size = New System.Drawing.Size(168, 23)
        Me.btnCancel.TabIndex = 0
        Me.btnCancel.Text = "Click me to cancel"
        '
        'Form2
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(264, 93)
        Me.Controls.AddRange(New System.Windows.Forms.Control() )
        Me.Name = "Form2"
        Me.Text = "Form2"
        Me.ResumeLayout(False)

    End Sub

#End Region

Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
    'stop processing until we confirm that user wants to cancel
    mt.Suspend()
    If MsgBox("Really cancel?", MsgBoxStyle.YesNo, "Are you positively sure?") = MsgBoxResult.Yes Then
        'the user means it. kill the thread.
        mt.Resume()
        mt.Abort()
    Else
        'resume the paused thread
        mt.Resume()
    End If
    Me.Close()
End Sub
End Class



在多个用户需要同时访问同样的对象的时候,产生多个线程来处理这些请求能够提高程序的性能。与数据库,消息队列,文件系统,或者其它程序进行异步通讯的应用程序也能从多线程中得到好处。


不利的一面
你是说必须使用catch语句么?说对了。但实际上还不止这些讨厌的地方:

记住在创建和切换线程的时候要消耗一定的CPU时间。因为一个进程的CPU时间是有限的,所以许多同时执行的线程可能导致这个程序比一个单线程的完成同样工作的程序要慢。因为线程对同样的地址空间都有访问权,它们有可能在同一时间访问全局变量。这可能导致无法预见的后果,在你的程序中产生无法隔离的错误。

解决第一个问题的方法包括仔细的计划和审查来确保最优的性能。对于第二个问题的解决引入了一个叫同步的主题,或者说阻止多个线程同时访问相同的资源。这会是下一篇文章的主题。

现在你应该知道如何实验.Net对线程的支持了。你只需要注意不要用剪刀。(线程在英文中和线是同一个词)。
 


百度大联盟认证黄金会员Copyright© 1997- CNET Networks 版权所有。 ZDNet 是CNET Networks公司注册服务商标。
中华人民共和国电信与信息服务业务经营许可证编号:京ICP证010391号 京ICP备09041801号-159
京公网安备:1101082134