设为首页 | 加入收藏 RSS订阅 | 高级搜索 | 收藏本站 | 论坛
用户名: 密码: 验证码: gdcode 注册
首页 | 财税咨询 | 税收实务 | 纳税辅导 | 税收优惠 | 税务策划 | 财税实务 | 财务研究 | 税务知识 | 财软 | 学院 | 下载 | 数据库技术 | 论坛
logo

在VisualBasic.NET中实现后台进程(一)

时间:2007-06-15 来源: 作者: 【字体: 减小 增大点击: 收藏 | 投稿
  

摘要:RockyLhotka建议并实现了一个结构化架构示例,该架构可用于充当辅助线程和UI线程之间的媒介,从而简化编写多线程辅助代码和UI以对其进行控制的过程。该架构包括可下载的代码示例,可以根据您的应用需要进行调整。

财.软联盟.fs119.net

使用多线程,可以使应用程序同时执行多项任务。使用多线程,可以让一个线程运行用户界面,让另一个线程进行复杂运算或在后台操作。由于Microsoft®VisualBasic®.NET支持多线程,因此我们很容易获得此功能。

财软.联盟.fs119.net

但多线程也有其不足之处。当应用程序使用多个线程时,我们总会遇到这样的问题:多个线程同时尝试与相同的数据或资源进行交互。出现这种情况时,问题就会变得非常复杂并且难以调试。

财软联,盟,fs119.net

更糟糕的是,多线程代码通常在最初开发期间似乎运行正常,但在生产过程中则会因为出现意外的情况(多个线程同时与相同的数据或资源进行交互)而导致失败。这样就增大了多线程编程的危险性! 财管家园,fs119.net

由于设计和调试多线程应用程序非常困难,因此Microsoft在COM中创建了“单线程单元”(STA)概念。VisualBasic6代码始终在STA中运行,因而代码只需考虑单线程即可。这样即可彻底避免共享数据或资源所带来的问题,但是同时也意味着,我们必须采取严格的措施才能利用多线程的优势。 财,软联盟,fs119.net

.NET中不会出现STA中的这种常见问题。所有.NET代码都在允许多线程操作的AppDomain中运行。这意味着VisualBasic.NET代码也在AppDomain中运行,因此可以使用多线程操作。显然,任何时候进行此操作都必须小心编写代码,以避免线程之间的冲突。 财管.家园.fs119.net

要避免线程之间发生冲突,最简单的方法就是确保多个线程永远不会与相同的数据或资源进行交互。尽管不太可能,但是对于任何多线程应用程序来说,应该在设计时尽量避免使用或尽量少使用共享数据或共享资源。 财软联 盟 fs119.net

这样不仅能简化编码和调试过程,还能提高性能。要解决线程之间的冲突,必须使用能够在某个线程完成操作之前阻止或暂停其他线程的同步技术。阻止线程也就是使线程处于空闲状态,不进行任何操作,因此会降低性能。

财管,家园,fs119.net

取消按钮和状态显示

在应用程序中使用多线程的原因有多种,但最常见的原因是我们一方面需要执行一个长时间运行的任务,另一方面又希望某些或所有用户界面对用户来说始终处于响应状态。 财软联.盟.fs119.net

至少我们应该使Cancel(取消)按钮始终保持响应状态,使用户能够通过它告诉系统,他们希望终止长时间运行的任务。 财管家 园 fs119.net

在VisualBasic6中,我们尝试使用DoEvents、计时器控件和许多其他方法进行该操作。VisualBasic.NET中的操作则简单得多,因为我们可以使用多线程。而且,只要我们小心谨慎,就可以完成此操作且不会使代码或调试复杂化。 财软联 盟 fs119.net

要在多线程环境中成功实现Cancel(取消)按钮,关键是要记住Cancel(按钮)的作用只是“请求”取消任务。由后台任务决定何时停止。 财管家园,fs119.net

如果我们实现一个能够直接停止后台进程的Cancel(取消)按钮,则可能会在执行某些敏感性操作的过程中将其停止,或者在后台进程关闭重要资源(例如,文件处理程序或数据库连接)之前将其停止。而这有可能导致严重后果,引起死机、应用程序行为不稳定或应用程序完全崩溃。

财管家.园.fs119.net

因此,Cancel(取消)按钮的作用应该只是请求停止后台任务。后台任务可以检查某一时间点上是否存在取消操作的请求。如果检测到取消操作的请求,后台线程则可以释放所有资源,停止所有重要操作并正常终止。 财 管家园 fs119.net

虽然请求取消操作非常重要,但是我们更希望能够通过UI为用户显示后台进程的状态信息。状态信息可以是文本格式的消息,也可以是完成任务的百分比,或者同时显示两种消息。 财 管家园 fs119.net

要在VisualBasic.NET中实现Cancel(取消)按钮或状态显示,我们所面对的最复杂的问题在于Windows窗体库不是对于线程并不安全。这意味着只有创建窗体的线程可以与该窗体或其控件进行交互。其他线程均不能安全地与该窗体或其控件进行交互。

财软联盟.fs119.net

但是,我们却无法避免编写多线程与给定窗体进行交互的代码。因此,运行时可能会产生不可预知的后果,甚至可能会导致应用程序崩溃。

财 软联盟 fs119.net

这要求我们在编码时必须小心谨慎,还要确保只有我们的UI线程与UI进行交互。为此,我们可以建立一个简单的架构,管理后台辅助线程和UI线程之间的交互。如果能够实现,则可以使UI代码和长时间运行的任务的代码都相对清楚地了解到我们正在使用多线程。

财管家,园,fs119.net

线程和对象

如果要创建一个后台进程并使其可以使用它自己的数据在它自己的线程上运行,最简单的方法是创建专门用于该后台进程的对象。虽然不一定能实现,但它是一个积极的目标,因为它能够大大简化多线程应用程序的创建过程。 财 管家园 fs119.net

如果后台线程在其自身的对象中运行,则后台线程可以使用该对象的实例变量(在类中声明的变量),而无须担心这些变量会被其他线程使用。例如,请考虑下面的类:

财管家,园,fs119.net

PublicClassWorker PrivatemInnerAsInteger PrivatemOuterAsInteger PublicSubNew(ByValInnerSizeAsInteger,ByValOuterSizeAsInteger) mInner=InnerSize mOuter=OuterSize EndSub PublicSubWork() DiminnerIndexAsInteger DimouterIndexAsInteger DimvalueAsDouble ForouterIndex=0TomOuter ForinnerIndex=0TomInner 'dosomecoolcalculationhere value=Math.Sqrt(CDbl(innerIndex-outerIndex)) Next Next EndSub EndClass
财软联盟,fs119.net

这个类适合在后台线程中运行,并且可以使用以下代码启动: 财软联,盟,fs119.net

DimmyWorkerAsNewWorker(10000000,10) DimbackThreadAsNewThread(AddressOfmyWorker.Work) backThread.Start()

财软联盟.fs119.net

Worker类中的实例变量可以存放其数据。后台线程可以安全地使用这些变量(mInnermOuter),还可以确保其他线程不会同时访问这些变量。 财管家 园 fs119.net

我们可以用其中包含的constructor方法使用任何起始数据初始化该对象。实际启动后台线程之前,我们的主应用程序代码会创建此对象的实例,并使用后台线程将要操作的数据对其进行初始化。

财软 联盟 fs119.net

后台线程将获取对象的Work方法的地址,然后开始启动。此线程将立即在对象内部运行代码,并使用该对象的专用数据。

财,软联盟,fs119.net

由于对象是自包含的,因此我们可以创建多个对象,每个对象在其自身的线程上运行并且对象之间相对独立。

财管.家园.fs119.net

但是,此实现方案并不理想。UI无法获得后台进程的状态。我们也未实现任何机制,使UI能够请求终止后台进程。 财,管家园,fs119.net

要解决以上两个问题,后台线程与UI线程之间需要以某种方式进行交互。这种交互方式非常复杂,因此最好能够以某种方式将交互放到一个类中,这样UI和辅助代码就不必为细节而担心。

财软联,盟,fs119.net

财,软联盟,fs119.net

财软联盟,fs119.net


财.管家园.fs119.net

财软联盟,fs119.net

财软 联盟 fs119.net

体系结构

我们可以创建使UI和辅助代码无需进行线程交互操作的体系结构。实际上我们可以实现此目标,还能实现一个能够通过某种方式实现复杂代码的架构,可以用来管理或控制后台线程及其UI交互。

财管家,园,fs119.net

我们先来讨论体系结构,然后再讨论如何设计和实现代码。从本文的相关链接可以下载此代码以及说明如何使用此代码的示例应用程序。

财软,联盟,fs119.net

通常情况下,应用程序中首先会启动一个单一线程,来打开用户界面。我们将其命名为“UI线程”以便于理解。“UI线程”是许多应用程序中的唯一线程,因此它要处理UI并完成所有操作。

财,软联盟,fs119.net

但是,现在我们创建一个“辅助线程”进行某些后台操作,让UI线程集中处理用户界面。这样即使辅助线程繁忙,UI线程也可以对用户保持响应状态。 财管家园 fs119.net

我们在UI线程和辅助线程之间插入一层代码,使其充当UI和辅助代码之间的接口。此代码实质上是一个“控制器”,用来管理和控制辅助线程及其与UI之间的交互。

财,软联盟,fs119.net

财软联,盟,fs119.net

图1:UI线程、控制器和辅助线程
财管家园,fs119.net

控制器包含的代码可以安全地启动辅助线程,将任何状态消息从辅助线程中转给UI线程,以及将任何取消请求从UI线程中转回辅助线程。UI代码和辅助代码不能直接交互,它们通常要通过控制器的代码进行交互。

财管 家园 fs119.net

但是辅助线程被激活“之前”和“之后”的时间段除外,这时UI代码可以与Worker对象进行交互。启动辅助线程之前,UI可以创建并初始化Worker对象。终止辅助线程之后,UI可以从Worker对象中检索任何值。从UI的角度看,将形成以下事件流: 财软联盟 fs119.net

  • 创建Worker对象。
  • 初始化Worker对象。
  • 调用Controller以启动辅助线程。
  • Worker对象将通过Controller将状态信息发送给UI。
  • UI可以通过Controller将取消请求发送给Worker对象。
  • Worker对象在完成操作后通过Controller通知UI。
  • 值可以直接从Worker对象中检索。
  • 除了在辅助线程处于激活状态时UI代码无法与Worker对象直接交互的限制外,对UI没有特殊的编码要求。即使正在运行后台操作,UI也会对用户保持激活和响应状态。

    财,管家园,fs119.net

    Worker对象的角度看,将形成以下事件流: 财,软联盟,fs119.net

  • UI代码创建Worker对象。
  • UI代码使用所需的数据初始化Worker对象。
  • Controller创建后台线程并调用Worker对象的方法。
  • Worker对象运行辅助代码。
  • Worker对象将状态信息传递给Controller,以便Controller将状态信息中转给UI。
  • Worker对象适时检查是否存在取消请求,如果存在,则停止运行。
  • Worker对象完成后,告诉Controller已完成,以便Controller将该信息中转给UI。
  • 现在辅助线程已终止,因此UI可以与Worker对象直接交互。
  • 由于辅助代码只与Controller交互,因此我们不必担心辅助线程会意外地与UI组件交互(这无疑会使应用程序不稳定)。现在,辅助代码依靠Controller与UI线程进行正确通信,因此各项操作都很安全。

    财.软联盟.fs119.net

    这意味着,只要处理好Worker对象中的实例变量,就无需处理辅助代码中的任何线程问题。

    财管,家园,fs119.net

    使用图表通常能够很好地了解不同组件(尤其是不同线程上的组件)之间的交互。Microsoft®Visio®支持创建UML(通用建模语言)图表,对理解很有帮助。 财管家园,fs119.net

    以下是说明UI、Worker对象和Controller之间事件流的UML序列图表。此图表假设不存在任何取消操作请求。WorkerController对象下面重叠在垂直线上的垂直活动栏突出了辅助线程上运行的代码。其他所有代码都在UI线程上运行。 财,管家园,fs119.net

    财软联盟,fs119.net

    图2:说明进程流的序列图表 财软,联盟,fs119.net

    使用UML活动图表也可以查看事件流。这种图表形式的着重点在于任务而不是对象,因此其中显示了发生的一系列步骤以及各步骤之间的流程。我们很容易看出UI代码如何停留在左侧的线程中,而Worker对象如何在右侧的线程上工作。Worker对象在其他线程中运行之前和运行之后可以直接由UI使用,以便初始化值,然后再检索结果。

    财,软联盟,fs119.net

    财软联盟 fs119.net

    图3:显示进程流的活动图表 财软.联盟.fs119.net

    使用这样的图表可以帮助我们找出后台线程处于激活状态时,UI与辅助线程(或反过来)无意中进行直接交互的位置。任何这样的交互都需要额外地进行编码,以避免出现可能使应用程序不稳定的错误。理想状态下,这种交互通过Controller组件来实现,我们可以在其中包含所有编码,使交互安全进行。 财软.联盟.fs119.net

    下图说明了UI发出取消请求时的事件序列。

    财软联盟 fs119.net

    财.管家园.fs119.net

    图4:显示取消请求的序列图表 财软.联盟.fs119.net

    请注意,取消请求从UI发送到Controller,然后Worker线程与Controller进行核实,确定是否发生了取消请求。UI和Controller都不会强制辅助代码终止,而是允许辅助代码自己正常安全地终止 财.软联盟.fs119.net

    财软.联盟.fs119.net

    财管.家园.fs119.net


    文章摘自网络,如有侵权,请与我们联系.
    数据统计中!!
    上一篇:VB.NET开发互联网应用
    下一篇:在VisualBasic.NET中实现后台进程(二)

    用户名: 密码: 匿名? 注册