注册 登录
Office中国论坛/Access中国论坛 返回首页

ganlinlao的个人空间 http://www.office-cn.net/?230471 [收藏] [复制] [分享] [RSS]

日志

Access菜鸟七大邪门武器之五:在access中使用多线程(三)

热度 1已有 8216 次阅读2016-4-3 10:44 |个人分类:access入门| access, 多线程

在这里我将万分感谢网友西门吹雪。如果不是他的辛苦和努力,我们很难想到在Access中用上多线程,至少在此之前,我从没想过。“别人的一小步,我们节省很长的一段路”。西门吹雪,谢谢你。


VB/COM 多线程概述

多线程程序

Visual Basic和COM

Thread Factory

异步调用

错误处理

调试

封装传送

ActiveX EXE服务器模型

同步和线程安全

TLS (Thread Local Storage,线程本地存储)

 

 

    这篇指南提供了一篇关于使用Visual Basic和Thread Factory开发多线程COM应用程序的概述。它假设读者对COM和COM线程模型(包括COM STA )已经有一定的了解。其中不会包含MTA(Multi Threaded-Apartment,多线程套间),主要是因为在Visual Basic不能创建MTA对象。

 

     一个多线程程序是一个可以在同一个应用程序当中同时执行2个或者更多的任务的程序 ,多任务是通过分别指派一个单独的线程来执行一个任务来实现的。一个线程基本上就是一个程序代码执行的一个路径,同时它也是在Win32调度器当中可以识别的最小的执行单元。一个进程由一个或者多个线程以及程序的代码、数据和程序中其他在内存 中的资源组成。Windows的调度器决定哪个线程应该运行以及什么时候运行。在多处理器计算机上,调度器可以通过切换单个线程到不同的处理器上来平衡CPU的负载。

 

    在使用得当的情况下,多线程技术可以明显提高一个应用程序的性能和反应速度,但如果使用不当,它也可以令一个程序变慢。经验是设计多线程程序的最好的老师,没有硬性规定什么时候应该使用多线程技术。可以通过仔细检查程序 ,来看看到底将哪个任务单独放到另外的线程中运行会更有用,又或者检查看看哪些任务如果同时运行会使程序更好,以上两点都是比较好的切入点。

 

    Visual Basic是一个可以用来创建Windows应用程序和COM ActiveX组件的开发工具。Visual Basic可以创建一个在COM STA 中安全运行的ActiveX DLL组件。Visual Basic在创建组件提供两种线程模型。它们是“单线程模型(Single Thread)”(所有对象都运行在一个STA中)以及“套间线程 模型(Apartment Threaded)”(每个对象都可以在一个各自不同的STA中运行)。尽管Visual Basic允许你创建基于套间线程模型的可以运行在另外套间的ActiveX DLL对象,但它总是使用同一个线程在主STA中创建这些对象。因此使用ActiveX DLL对象的Visual Basic应用程序是单线程的。

 

    Thread Factory™提供一个 类库,用于专门通过在另外的STA套间中中创建COM对象(使用另外的线程) 的方式来克服这个限制。在不同于应用程序主线程的线程中创建的对象,我们称之为工作者对象(Worker Objects)(即上边运行着工作者对象的工作者线程(Worker Thread))。

    Thread Factory 中有一个AsyncObject对象(Asynchronous Object,异步对象)是专门用来创建工作者对象的。一个AsyncObject对象是 一个轻型包装类,它用于在一个单独的STA套间中创建工作者对象,而且这个STA套间运行在工作者线程上。除了创建工作者对象以外,AsyncObject对象也会接管所有和这个工作者对象有关的来 往传输的COM调用、异步调用和异步调错误处理。每个工作者对象都被一个单独的AsyncObject对象包裹住。

注: Thread Factory只能够为那些被标记为支持Apartment线程模型或者两者线程模型都同时支持的对象创建工作者对象。

 

普通的Visual Basic对象创建过程(单线程)

 

Dim oPerson as clsPerson

Set oPerson = new clsPerson     '对象在 主STA 及线程中创建

 

Thread Factory的对象创建过程(多线程)

 

Dim oAsyncObject as new ThreadFactoryLib.AsyncObject

Dim oPerson as clsPerson

Set oPerson = oAsyncObject.Create("PersonLib.clsPerson")    '对象在另外的STA及线程中创建

 

    当用一个AsyncObject对象创建一个新的 对象(工作者对象)时, 开始它首先会在一个另外的线程上创建一个新的STA套间,然后会在新的STA套间中创建工作者对象的一个实例。

 

    备注: 如果在一个工作者对象的代码中使用New关键字来创建一个其他的新的工作者对象,那这个工作者对象会和新创建的工作者对象共享同一个套间和线程。

 

    如果关闭应用程序的主线程(主STA),则会引起所有的工作者都同时关闭。

 

    要想产生一个真正的多线程程序,通过在一个另外的线程中运行工作者对象这样的方式这是不够的。因为所有的Visual Basic COM调用都是同步的,这使得两个线程不可能同时运行(除非你使用定时器来欺骗并且绕过COM)。AsyncObject对象公开了一个称之为IBlindDelegator的特殊接口 ,这个接口使得要实现 异步调用变更得更容易。这个IBlindDelegator接口会通过Begin_和Finish_方法来平行化COM+异步调用。

 

    我们应用异步调用时,需要使用特殊的错误处理方式。错误一般是通过调用堆栈来返回的,而由异步调用引起的错误会被丢弃,这是因为那时的调用堆栈是空的,而那时 我们没有一个可以将这个错误返回的上层函数。由于所有由Thread Factory创建的工作者对象都在AsyncObject对象中包装着,AsyncObject对象会拦截所有在异步调用时所产生的错误并且将他们转换为AsyncObject对象的OnError事件,这样他们就不会被丢弃了。

 

    在应用程序的开发过程中,最重要的步骤之一就是调试。Thread Factory通过允许你安全的使用Visual Basic的调试器,从而使调试变得简单。由于Visual Basic的调试器是为单线程应用程序的调试而设计的,在调试不同的多线程应用程序的项目时,都会有一些不同的限制。

 

当调试一个不包含ActiveX DLL源代码的工程时:

    • 工作者对象会在一个另外的线程它自身的STA中创建,但这时这个被调试的工程当中产生的所有对于这个工作者对象的调用都会被串行化(即使使用IBlindDelegator接口也是这样)。

当调试一个包括ActiveX DLL源代码的工程时:

    • Visual Basic会强制所有的工作者对象都在同一个主STA线程中创建。调试这一类项目是和调试一个普通的单线程应用程序是一模一样的。

    提示: 微软的Visual C++调试器是一个非常好的用来代替Visual Basic的调试器的代替品。不像VB的调试器,Visual C++的调试器设计时就有考虑到多线程程序的调试。要使用Visual C++来调试VB的应用程序和组件,你首先必须要编译项目时勾选“产生符号化调试信息”选项。

 

    备注: 当你运行完一个多线程的工程后关闭Visual Basic的IDE时,可能会引发一个异常(见下图)。这是因为可能在调试时我们曾经以按下“停止”按钮的方式来中断调试过程,又或者在程序代码中包含了END语句 ,并且试图以它来中断程序的运行。这会导致Visual Basic没有正常让清理代码执行,从而出现了下面的异常。这个异常只会在你关闭IDE时出现,绝对不会在运行编译后的程序时出现。一个尽量减少这种偶然错误的 方法是,在另外的DLL的工作者对象它们的创建和销毁两个事件过程中加入相应的代码来处理这些工作。这样之所以能解决问题,是因为类的Terminate事件它不管我们在什么样的情况下退出程序 ,它都会被执行。Progress示例程序就体现了这点(译者注:这一段我没有按照完全原文逐字翻译,而按照我的理解来译)。

 

 

    当要和一个另外的线程或者进程通信时是会用到封装传送的。封装传送是在进行超出线程或进程边界的方法调用时,完成打包和发送的行为 的体现。COM使用 Proxy Stub 来封装传送那些在线程/进程间的调用。

 

    在由Thread Factory所创建的工作者对象之间通信速度是非常快的,这是因为这些工作者对象都是在同一个进程里的不同线程间运行的(In-Process,进程内)。 而ActiveX EXE 服务器模型虽然也可以创建工作者对象,但是 在它创建的进程之间通信非常慢,这是因为在调用外部进程时会产生额外的针对调用的封装传送工作(Out-Of-Process,进程外)。

 

    下边的插图展示了在Thread Factory和ActiveX EXE服务器模型两种模型中,产生与其他工作者对象通信的情况时涉及的封装传送流程。

Thread Factory

Active X EXE 服务器模型

 

    尽管Thread Factory和ActiveX EXE服务器模型两种模型都是创建工作者对象的有效方法,但是Thread Factory提供很多ActiveX EXE服务器模型所没有的优势,包含:

  • 性能:使用Thread Factory的通信速度快很多

  • 内置异步调用架构(包含取消调用的操作)

  • 内置异步错误处理

  • 异步调用超时及强制结束处理

  • 支持单独检查每个工作者对象的状态

  • 很容易在一个单独的Visual Basic会话中调试

  • 支持处理特定的资源,比如UDTs(译者注:用户自定义结构体)、hDC句柄、临界区域和hEvents句柄

  • 支持创建多线程OCX组件

    同步问题在开多线程应用程序时非常重要 ,这是因为它可以保证线程的安全。我们通常使用同步对象来防止多个线程在同时访问同一个变量或者系统资源时发生冲突问题。Thread Factory所创建的工作者对象,由COM所提供的一个特性来解决同步问题,这个特性主要指COM强制所有的工作者对象都必须在自身的STA中创建,并且COM串行化所有 外部对它们的访问(译者注:这一句是按照我自身的理解调整翻译的,没有完全按照原文一字一词的译)。串行化调用天生就很安全,这是因为在给定的任意时刻只有内 ,所有的对于对象的调用当中,只能有单个方法被执行。如果两个工作者对象试图同时调用同一个另外的工作者对象的话,则两者的调用都会被串行化,由此,只有其中一个工作者对象会得到他们想要的 ,而另一个工作者对象的操作则必须等到第一个调用操作执行完成后才会被执行。

    很多时候在非线程安全的情况下,你必须手动添加代码以防止发生同时对一个资源或者对象的访问冲突问题。在一个多线程程序中资源或者对象不是线程安全的原因有很多,但通常通过串行化访问可以使得它 们变成线程安全的。这可以通过使用Windows提供的众多同步对象当中的某一种来实现。为了方便起见,Thread Factory在它的类库中提供了一些关于临界区域(Critical Sections)和互斥量(Mutexes) 的API调用声明(译者注:你可以直接在VB中使用这些技术而且不需要额外的声明它们)——详见Win32 API调用

 

    在开发多线程应用程序时全局变量是另外一个问题,这是因为全局变量是暴露的,这使得所有的对象(包括工作者对象)都可以同时访问它。Thread Factory的工作者对象使用TLS(Thread Local Storage,线程本地存储)技术,来每为个线程都保存一份全局变量的单独的拷贝。

 

    为了理解线程间的通信,我们有必要知道一个对象生存的地方(具体在哪个STA套间)。和一个工作者对象进行任何的通信都总是被封装传送到工作者对象所在的对应套间。每个STA套间都有它自己的线程,而且这个线程是 唯一一个可以让存在于这个套间的任意对象执行代码的线程。

 

    总之,Thread Factory提供了一个较全面的解决方案来用于创建健壮的多线程Visual Basic应用程序。我们建议你把 指南 部份详细阅读一遍,它会引导你一步一步地完成创建工作者对象和对他们的异步调用的整个过程。


什么是Thread Factory™ ?

    Thread Factory™是一个用于让程序员能非常容易的创建一个健壮的Visual Basic多线程应用程序的一个DLL组件库。Thead Factory使得Visual Basic程序员们能创建出运行在另外的线程的ActiveX对象。Thead Factory也包含一个较完痒的用来调用和取消异步调用的框架。在Thead Factory众多著名的特点当中,最显著的是它的健壮性、性能表现和易用性。Thead Factory遵循COM的所有规则,而且避免使用任何绕过COM组件的技术。和试图通过创建另外的进程来实现模拟多线程的ActiveX EXE服务器的产品不同,Thread Factory™会创建真正运行在同一个进程内的多线程VB6应用程序和组件。以下是两种架构的一个比较。

 

任务和特点

Thread Factory™

ActiveX EXE

初始化与启动

(毫秒级)

(通常5-15秒)

对象创建+Marshalling

(进程内,In-Process)

非常慢 (进程外,Out-Of-Process)

支持在单个VB6 IDE会话中调试

内置异步调用

内置异步错误处理

内置取消异步调用操作

支持创建多线程OCX 控件

要求实现特定的接口

共享进程特定资源( hDC、hWnd、内存地地址)

 

总之,Thread Factory™允许开发者快速而经济的在所有微软Windows操作系统上创建结实而快速的多线程应用程序和组件。

 

 新特色:

一般特点:

  • 可安全使用VB6的IDE进程调试

  • 支持可使COM+平衡化的异步调用(Begin_ 和 End_)

  • 加强的异步调用错误处理

  • 包含StopWatchPro™(译者注:秒表)多线程OCX控件(现在支持VBA)

  • 新的 ElapsedTime 类——高精度定时(精确到1毫秒)

  • 支持取消异步调用操作,详见 CancelObject

  • 支持创建多线程OCX控件,详见 多线程OCX控件示例

  • 支持完全可配置的线程优先级机制,详见 SetThreadPriority

  • 新的 ThreadHANDLEThreadID 属性

  • 新的运行时模块(大小共160K),详见 发布应用程序

  • 支持轻松访问有用的Win32 API函数和同步对象

 

架构:

    Thead Factory组件库使用C++(ATL)和汇编语言开发。

注: Thead Factory不依赖COM+或者任何MFC组件。

  

    Thead Factory兼容COM技术,并且能够被如微软的VB6、Excel、Access和.NET等不同的支持COM组件技术的开发工具支持。程序员如果是使用像Excel、Access或者.NET这样的IDE环境来开发程序的话,应当使用代理对象设计模式来进行设计。


发表评论 评论 (1 个评论)

回复 tmtony 2016-4-23 08:39
这个真的能够使用吗?

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

QQ|站长邮箱|小黑屋|手机版|Office中国/Access中国 ( 粤ICP备10043721号-1 )  

GMT+8, 2024-4-20 14:54 , Processed in 0.056133 second(s), 18 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

返回顶部