怎样实现多线程编程?(急!急!急!)

wthai 2000-08-20 08:50:00
目前我正在作一项目(会议签到系统),涉及到与外设(刷卡机)的有关编程。我的问题是:怎样实现自动签到即刷卡签到与手工签到同时处理?是不是涉及到多线程编程问题?
如果涉及到多线程编程问题,在VB中怎样处理?是对什么事件编程呢?能提供源代码者送大分!!!!
...全文
710 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
bluntknife 2000-08-31
  • 打赏
  • 举报
回复
我也正在做类似的软件,积累了一些心得,如果有兴趣可以把你要做的软件的具体情况 E-Mail 给我。
wthai 2000-08-30
  • 打赏
  • 举报
回复
我想补充一点,我的卡和卡的读写器是向厂家购买的,且厂家已对卡进行了底层开发,有现成的
初始化,读卡,写卡函数供我调用。我主要是进行二次开发。
0xFFCD 2000-08-28
  • 打赏
  • 举报
回复
Visual Basic 的单元模型线程


在 Visual Basic 中,单元模型(apartment-model)线程用于保证线程的安全性。单元模型线程的每个线程就好象是一个单元,在该线程上创建的所有对象都存活于该单元之中,它不会觉察到其它单元中的对象。

如图 8.3 所示,Visual Basic 为每个单元复制了一份全局数据,从而减少了由于从多个线程访问全局数据而发生的冲突。

图 8.3 每个线程有一份自己的全局数据副本



这意味着不能使用全局数据在不同线程的对象之间进行通讯。

注意 只有客户端将对象的引用分别传给对方,不同单元的对象才能互相通讯。在这种情况下,使用线程间调度来提供同步。线程间调度几乎和进程间调度一样慢。

注意 除了维护全局数据的独立拷贝以外, Sub Main 过程会在每个新的单元(即每个新的线程)中执行。否则,就无法为每个线程初始化全局数据。在“设计线程安全 DLL”和“设计多线程进程外部件” 中有关于这个问题的进一步的讨论。


单线程的部件与单元模型
Visual Basic 创建的所有部件都使用单元模型,无论该部件是单线程的还是多线程的。单线程的部件只有一个单元,该单元包含部件提供的所有对象。

这就意味着用 Visual Basic 创建的单线程的 DLL 可以安全地用于多线程的客户端。不过,性能与安全是一对矛盾,这种安全性损失了性能。除了当前的客户端线程之外,其它所有的客户端线程的调用都要等待被调度,就好象这些调用是进程外调用。请参阅“设计线程安全的 DLL”。

线程的所有权
多线程的进程内部件没有自己的线程。如“设计线程安全的 DLL”中所述,定义每个单元的线程都属于客户端。

与此相对的是,多线程的进程外部件可能有一个固定线程数目的线程池,或者为每个外部创建的对象设置一个线程。请参阅“设计多线程的进程外部件”。

获取 Win32 的线程 ID
除了为每个线程维护一份全局数据副本之外,Visual Basic 还要维护由 App 等全局对象提供的数据的单独副本。因此 App 对象的 ThreadID 属性总是返回负责处理该属性调用的线程的 Win32 线程 ID。

使用线程的位置
和 Visual Basic 以前的版本不同,用 Visual Basic 98 编写的工程可以享受到单元模型线程化的优点,而不再需要牺牲诸如窗体和控件之类的可视化元素。Forms、UserControls、UserDocuments 以及 ActiveX 的设计器都是线程安全的。在为 Threading Model 选项选择设置时不必把工程标记为“无用户界面执行”。

要设置 ActiveX DLL 、ActiveX Exe 或 ActiveX 控件工程的线程模型,请按照以下步骤执行:

在“工程”菜单上,选择“<project> 属性”来打开“工程属性”对话框。


在“通用”选项卡上,选择“线程模块”框中需要的选项。
对于 ActiveX DLL 和 ActiveX 控件工程,可以选择“单元线程”或“单线程”。


对于 ActiveX Exe 工程,既可以指定每个新对象在一个新线程上创建(Thread per Object),也可以把部件限制在固定的线程池上。线程池大小为 1 则工程变为单线程的;线程池数目大于 1 则使工程变为单元线程的。
注意 当改变现有工程的线程模式时,如果工程使用了单线程 ActiveX 控件,则会产生一个错误。Visual Basic 禁止在使用单元线程的工程中使用单线程控件,这将在下面的“将现有的工程转换为单元模式线程化的工程”中说明。

注意 如果为 ActiveX Exe 工程指定了“每个对象对应一个线程”(或者是数目多于一个的线程缓冲区),那么只有外部创建的对象才会在新线程中创建。(请参阅“设计多线程进程外部件”。)对于 ActiveX DLL 和 ActiveX 控件工程,“每个对象对应一个线程”和“线程缓冲池”是不可用的,这是因为线程的创建是由客户应用程序控制的。

重点 对于专业版和企业版的用户,关于选择“每个对象对应一个线程”或“线程缓冲池”的效果的详细讨论,请参阅“设计多线程进程外部件”。

设置无用户界面执行
如果需要在网络服务器上创建不用操作者干预就能运行的部件,那么应该设置“无用户界面执行”选项。选择“无用户界面执行”不会影响部件的线程模式。

要将 ActiveX DLL 或 EXE 工程标记为无用户界面执行,请按照以下步骤执行:

在“工程”菜单中单击“<project> 属性”打开“工程属性”对话框。


在“通用”选项卡中,选取“无用户界面执行”,然后单击“确定”。
重点 选择无用户界面执行选项将牺牲所有需要用户交互的窗体,其中包括消息框和系统错误对话框。请参阅“多线程部件的事件日志”。

单元模型线程化的限制
Visual Basic 中的单元模型线程化具有下述限制。

开发环境不支持多线程调试。如果有 Microsoft Visual Studio ,可以先把单元线程化的部件编译成带调试信息的本机代码,然后使用 Visual Studio 的调试程序来进行调试。如果是测试和调试进程内部件(.dll 和 .ocx 文件)则还需要一个多线程的客户应用程序。


MDI 父窗体和子窗体共享数据时所必须采用的方法是很难保证线程安全的。因此,在单元线程化的工程中不允许出现 MDI 窗体。对于单元线程化的 ActiveX DLL工程以及选择了“每个对象对应一个线程”(或者线程缓冲区数目多于一个)的 ActiveX Exe 工程,在“工程”菜单上的“添加 MDIForm”将是不可用的。如果在包含 MDI 窗体的工程中把线程模式改成“单元线程”,则会出现错误。


除了性能比较差以外,单线程的 ActiveX 控件会在多线程的客户应用程序中导致很多问题。因此Visual Basic 禁止在线程模式被设置成“单元线程”的工程中使用单线程控件。详细信息请参阅下面的“将现有的工程转换为单元模式线程化的工程”。


友元属性和方法只能由同一个线程上的对象调用。因为它们不是对象公有接口的一部分,因此无法在线程之间进行调度。


在 ActiveX Exe 工程中的 ActiveX 文档对于单元模型不是线程安全的,除非选择了“每个对象对应一个线程”或者缓冲区数目多于一个的“线程缓冲池”选项。


如果线程显示了一个具有 vbModal 属性的窗体,那么该窗体将只对该线程上的代码和窗体是模态的。在其它线程中运行的代码将不会被阻塞,且由其它线程显示的窗体也将保持是活动的。


在窗体和控件之间的拖放操作只有在拖放源和拖放目标处于同一线程中时才有效。(不过,OLE 的拖放是通用的。)


窗体之间的 DDE 只有在窗体处于同一线程中时才有效。
将现有的工程转换为单元模式线程化的工程
通过更改“线程模式”选项并重新编译工程,就可以为现有的工程添加单元线程化功能。更改“线程模式”选项的操作可以参见“为工程选择线程模式”中的说明。对于很多工程而言,这就是所需的全部操作。

如果一个现有的 ActiveX DLL、ActiveX EXE、或 ActiveX 控件工程使用了具有单线程成分的控件,那么把“线程模块”的设置改成“单元线程”就会产生一个错误。由于单线程的 ActiveX 控件在多线程客户应用程序中会引起大量严重的问题,因此 Visual Basic 不允许在 ActiveX 部件工程中使用它们。

如果现有的工程使用了单线程的控件,那么请与代理商联系,以了解是否有单元线程化的版本。

强制使用单线程控件
通过手工编辑 .vbp 文件,有可能能骗过 Visual Basic,从而在一个单元线程化的工程中可以使用单线程控件。请不要这么做。单线程 ActiveX 控件可能导致的问题包括:

如果用户通过 TAB 键聚焦到窗体上的一个单线程的控件上,且这个窗体是运行在不同的线程上的,那么用户将无法再从这个控件上通过 TAB 键将焦点移走。出现这种情况是因为单线程控件的线程中没有关于这个窗体的线程中焦点的上下文。在这种情况下,用户要改变焦点的唯一方法就是通过鼠标。将单线程控件作为单元线程化控件的一个元素使用也会导致类似的问题。


在多线程应用程序中,如果窗体处于不同的线程中,那么无法通过单击一个单线程控件来激活该窗体。在单击单元线程化控件的单线程元素时也会出现这种情况。


在多线程应用程序中使用单线程 OCX 会产生性能上的问题,因为 OCX 提供的所有控件必须在同一个线程中运行。这就是说,对于运行在不同线程中的窗体上的控件,所有的通信都需要昂贵的交叉线程调用。使用单线程元素控件的单元线程化 ActiveX 控件也会导致类似的性能问题。


在多线程应用程序中, TAB 键和访问键(例如Alt+A )对于不在应用程序主线程中的单线程控件来说都是无效的。使用单线程控件元素的单元线程化 ActiveX 控件也会有同样的问题。


如果单元线程化控件设置了单线程控件元素的 Picture 属性(或者带有 Picture 对象的其它属性),多线程的客户应用程序将会出现错误。这是因为在线程之间无法调度 Picture 对象。
重点 在用 Visual Basic 或其它开发工具建立的所有多线程部件或应用程序中,单线程控件都可能导致这样或那样的问题。

重入
在单元模型中,重入是指按下述顺序发生的事件:

由于调用了对象的属性或方法,单元的执行线程进入该对象的代码部分。


当线程正位于该属性或方法内时,另一个线程也调用了这个对象的属性或方法,且自动化序列化了该请求,就是说,它将该请求放入队列之中,直至拥有该对象的单元的线程结束目前正在执行的成员函数。


在终止目前的成员函数之前,线程执行了某些代码,放弃对处理器的控制权。


自动化通知线程开始执行已序列化的请求,因此该线程重入对象的代码。
新的请求需要执行的成员函数可能是别的成员函数,也可能是线程重新进入正在执行的成员函数。在后一种情况下,线程将两次进入同一个成员函数。如果第二个成员函数不放弃对处理器的控制权,则它将在第一个成员之前完成处理。如果它改变了第一个成员函数正在使用的模块级数据,结果可能会很糟。

通过对每个单元进行属性和方法调用的序列化,自动化可以防止重入,除非代码自己放弃了对处理器的控制。可导致放弃对处理器的控制的代码有:

调用 DoEvents。


调用其它线程的对象的方法或属性,或另一个进程的对象的属性或方法。


产生需要由另一个线程或另一个进程处理的事件。


从一个方法内调用一个交叉线程或交叉进程方法。


显示窗体。
如果希望两个成员函数能够安全地同时执行,除非在编写对象的代码时必须进行特殊的处理,否则在代码中不要放弃对处理器的控制权。

详细信息 单元模型线程对进程内部件和进程外部件的影响是不同的,请参阅“设计线程安全的 DLLs” 和“设计多线程的进程外部件”。
0xFFCD 2000-08-28
  • 打赏
  • 举报
回复
创建多线程的测试应用程序


为了测试和调试进程内部件(.dll 和 .ocx 文件),需要一个多线程的客户应用程序。创建一个简单的多线程应用程序的步骤如下:

打开一个新的 ActiveX EXE 工程,将默认的类模块命名为 MainApp 。把 MainApp 的 Instancing 属性设成为 PublicNotCreatable 。MainApp 对象将占有这个应用程序的第一个线程,并显示主用户界面。


在“工程属性”对话框的“通用”选项卡上,在“启动对象”框中选择“Sub Main”,在“线程模块”框中选择“每个对象对应一个线程”,并输入一个具有唯一性的工程名称。(工程名称决定类型库的名称;如果两个应用程序的类型库名称相同的话将会出现问题。)"ThreadDemo" 是下例所用的工程名称。在“部件”选项卡上,在“启动模式”框中选择“独立方式”。


增加一个窗体,将它命名为 frmProcess ,并将其 Visible 和 ControlBox 属性设成 False 。 这个窗体将以隐藏窗口的方式运行,其中 Sub Main 用来标识该进程的主线程。这个窗体不需要代码。


在工程中增加一个标准模块。把声明、 Sub Main 过程、以及下面显示的 EnumThreadWndMain 过程放在这个模块中。正如在相应的文字和代码注释中所说明的,在启动应用程序以及每次创建一个新的线程时,都要执行 Sub Main 。 Sub Main 的示例代码演示了如何标识第一个线程,这样就能知道何时创建 MainApp。


增加一个窗体,将它命名为 frmMTMain 。这个窗体为这个测试应用程序提供主用户界面。在这个窗体中增加简单的声明,并把紧在“测试应用程序的多个实例”标头上面的 Form_Unload事件也加进去。


在 MainApp 的 Class_Initialize 事件过程中增加代码以显示 frmMTMain。详见下面的代码。


要创建另外的测试线程,则在工程中应该至少有一个 Instancing 属性被设成 MultiUse 的类。增加一个类模块和一个窗体,插入“创建新线程”标头下的代码。由于这个工程选择了“每个对象对应一个线程”,因此每个在外部创建的公共对象都会启动一个新的线程。这就是说,可以通过使用 CreateObject 函数创建带程序标识符( ProgID )的 MultiUse 类的实例,来创建一个新的线程,见相应文字中的说明。


向 frmMTMain 中增加代码,通过创建所定义的 MultiUse 类的实例来创建新的线程。有关代码在下面这个示例的“创建新线程”标头下。


开发环境不支持多线程。如果按 F5 键运行工程,所有的对象将被创建在同一个线程中。为了测试多线程的行为,必须编译 ActiveX EXE 工程,然后运行最终的可执行程序。
重点 为了保证每个新的 MultiUse 对象都能启动一个新线程,必须使用“每个对象对应一个线程”选项而不能用“线程缓冲池”选项。

在 Sub Main 中决定主线程
每个新的线程都会执行 Sub Main 。这是因为 Visual Basic 为每个线程(即每个单元)都维护了一个全局数据的独立副本。为了初始化线程的全局数据,必须执行 Sub Main 。这就是说如果 Sub Main 加载了一个隐藏的窗口,或者显示了应用程序的主用户界面,那么在创建每个新线程时都会加载这些窗体的新副本。

下面的代码用来判断 Sub Main 是不是在第一个线程中执行,这样可以只加载一次隐藏的窗体或者只显示一次测试应用程序的主用户界面。

' 被隐藏窗口的标题的根值
Public Const PROC_CAPTION = "ApartmentDemoProcessWindow"

Public Const ERR_InternalStartup = &H600
Public Const ERR_NoAutomation = &H601

Public Const ENUM_STOP = 0
Public Const ENUM_CONTINUE = 1

Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Declare Function GetWindowThreadProcessId Lib "user32"_
(ByVal hwnd As Long, lpdwProcessId As Long) As Long
Declare Function EnumThreadWindows Lib "user32" _
(ByVal dwThreadId As Long, ByVal lpfn As Long, ByVal lParam As Long) _
As Long

' 通过 EnumThreadWindows 取得窗口句柄。
Private mhwndVB As Long
' 用来标识主线程的隐藏窗体。
Private mfrmProcess As New frmProcess
' 进程标识符。
Private mlngProcessID As Long

Sub Main()
Dim ma As MainApp

' 借用一个窗口句柄来获得进程
' 标识符(请参阅下面 EnumThreadWndMain 的回调)。
Call EnumThreadWindows(App.ThreadID, AddressOf EnumThreadWndMain, 0&)
If mhwndVB = 0 Then
Err.Raise ERR_InternalStartup + vbObjectError, , _
"Internal error starting thread"
Else
Call GetWindowThreadProcessId(mhwndVB, mlngProcessID)
' 进程标识符使隐藏窗口的标题具有唯一性。
If 0 = FindWindow(vbNullString, PROC_CAPTION & CStr(mlngProcessID)) Then
' 找不到窗口,因此这是第一个线程。
If App.StartMode = vbSModeStandalone Then
' 用唯一的标题创建隐藏窗体。
mfrmProcess.Caption = PROC_CAPTION & CStr(mlngProcessID)
' MainApp 的初始化事件( Instancing =
' PublicNotCreatable )显示主用户界面。
Set ma = New MainApp
' (如果没有对 MainApp 的全局引用,那么
' 关闭应用程序就更加简单;否则 MainApp 应该
' 把 Me 传递给主用户窗体,这样
' 该窗体就能保证 MainApp 不被终止。)
Else
Err.Raise ERR_NoAutomation + vbObjectError, , _
"Application can't be started with Automation"
End If
End If
End If
End Sub

' EnumThreadWindows 所使用的回调函数。
Public Function EnumThreadWndMain(ByVal hwnd As Long, ByVal _
lParam As Long) As Long
' 保存窗口句柄。
mhwndVB = hwnd
' 只需要第一个窗口。
' 一发现窗口就停止迭代。
EnumThreadWndMain = ENUM_STOP
End Function

' MainApp 在它的 Terminate 事件中调用这个子程序;
' 否则隐藏窗体将使
' 应用程序免于被关闭。
Public Sub FreeProcessWindow()
Unload mfrmProcess
Set mfrmProcess = Nothing
End Sub

注意 这种以来标识第一个线程的技术在 Visual Basic 将来的版本中可能会有问题。

可以看到 Sub Main 在第一次以后对于任何线程都不再有任何动作。在增加创建 MultiUse 对象的代码(以便启动后继的线程)时,应该确保包含了初始化这些对象的代码。

EnumThreadWindows 和回调函数 EnumThreadWndMain 一起使用,以便能确定 Visual Basic 为其内部使用而创建的一个隐藏窗口的位置。这个隐藏窗口的窗口句柄被传递给 GetWindowThreadProcessId,该函数返回进程标识符。进程标识符将被用来创建由 Sub Main 加载的隐藏窗口 (frmProcess) 的唯一标题。后继线程检测到这个窗口后就能知道它们不需要再创建 MainApp 对象了。这种转换是必需的,因为 Visual Basic 没有提供识别应用程序主线程的方法。

MainApp class 在其 Initialize 事件中显示测试应用程序的主窗体。MainApp 应该把它的 Me 引用传递给主窗体,这样该窗体就能保证 MainApp 不被终止。从主用户界面可以创建所有的后继线程。将 MainApp 的 Instancing 属性设成 PublicNotCreatable 能有助于避免显示两个用户主界面的窗体。

下面是 MainApp 类和它的相关窗体(上面步骤5和6)的简单的示例:

' MainApp 类的代码。
Private mfrmMTMain As New frmMTMain

Private Sub Class_Initialize()
Set mfrmMTMain.MainApp = Me
mfrmMTMain.Caption = mfrmMTMain.Caption & " (" & App.ThreadID & ")"
mfrmMTMain.Show
End Sub

Friend Sub Closing()
Set mfrmMTMain = Nothing
End Sub

Private Sub Class_Terminate()
' 清理隐藏窗口。
Call FreeProcessWindow
End Sub

' frmMTMain 窗体的代码。
Public MainApp As MainApp

Private Sub Form_Unload(Cancel As Integer)
Call MainApp.Closing
Set MainApp = Nothing
End Sub

测试应用程序的多个实例
在隐藏窗口的标题中包含进程标识符能够使测试应用程序的多个实例互不影响地运行。

如果调用了 CreateObject ,那么所创建的公有类的实例将会位于当前应用程序实例的一个线程上。这是因为在寻找其它正在运行的能够提供该对象的Exe 部件之前,CreateObject 总是试图在当前应用程序中创建对象。

单元的有用属性
把进程标识符作为包含 Sub Main 的模块的只读属性将是有用的:

'测试应用程序中不需要这些代码
Public Property Get ProcessID() As Long
ProcessID = mlngProcessID
End Property

这使得线程上的所有对象都能通过调用非限定的 ProcessID 属性来取得进程标识符。同样,以这种方式将 Boolean IsMainThread 属性显露出来也是有用的。

创建新线程
“每个对象对应一个线程”选项使每个在外部创建的公有对象——即使用 CreateObject 函数创建的对象——都会启动一个新的线程。要创建一个新线程,可以简单地使用一下某个 MultiUse 类的程序标识符 (ProgID):

'在测试应用程序中不需要包含这些代码
Dim tw As ThreadedWindow
Set tw = CreateObject("ThreadDemo.ThreadedWindow")

这时变量 tw 包含了对一个新线程中的对象的引用。所有使用 tw 对这个对象的属性和方法的调用都会导致交叉线程调度的额外开销。

注意 用 New 操作符创建的对象不是在新建的线程中创建。它驻留在执行 New 操作符的同一个线程中。请参阅“设计多线程进程外部件”和“在 Visual Basic 部件中对象创建是如何工作的”。

为了确保在所有其它线程结束前 MainApp 不终止,可以给每个公有类设一个 MainApp 属性。如果对象在新线程中创建了一个 MultiUse 对象,那么作为初始化过程的一部分,它可以把对 MainApp 对象的引用传递给新对象。(也还可以向 MainApp 传递一个对新对象的引用,这样 MainApp 就能有一个对所有控制该线程的对象的引用集合了;但是要记住这可能会产生循环引用。请参阅“处理循环引用”。)

如果希望控制一个线程的类来显示一个窗体,那么应该向它提供显示窗体的 Initialize 方法(不要和 Initialize 事件混淆)或 Show 方法。不要在 Class_Initialize 事件过程中显示窗体,因为这样会在创建类的实例时产生时序错误。下面是的代码是关于一个 MultiUse 的 ThreadedWindow 类和它的窗体的一个很简单的实例:

' 一个 MultiUse 的 ThreadedWindow 类的代码。
Private mMainApp As MainApp
Private mfrm As New frmThreadedWindow

Public Sub Initialize(ByVal ma As MainApp)
Set mMainApp = ma
Set mfrm.ThreadedWindow = Me
mfrm.Caption = mfrm.Caption & " (" & App.ThreadID & ")"
mfrm.Show
End Sub

Friend Sub Closing()
Set mfrm = Nothing
End Sub

' frmThreadedWindow 窗体的代码。
Public ThreadedWindow As ThreadedWindow

Private Sub Form_Unload(Cancel As Integer)
Call ThreadedWindow.Closing
Set ThreadedWindow = Nothing
End Sub


下面的代码段显示了如何初始化 ThreadedWindow 对象:

'测试应用程序的主窗体( frmMTMain )代码。
Private Sub mnuFileNewTW_Click()
Dim tw As ThreadedWindow
Set tw = CreateObject("ThreadDemo.ThreadedWindow")
' 告诉新对象显示它的窗体,并
' 将一个对主应用程序
' 对象的引用传递给它。
Call tw.Initialize(Me.MainApp)
End Sub

如果有很多可以控制线程的类,那么可以通过定义包含 Initialize 方法的 IApartment 接口来使代码更通用。在实现每个类的 IApartment 时,可以为每个类提供适当的 Initialize 方法。下面是创建线程的代码实例:

'测试应用程序中不需要这些代码
Private Sub mnuFileNewObject_Click(Index As Integer)
Dim iapt As IApartment
Select Case Index
Case otThreadedWindow
Set iapt = CreateObject("ThreadDemo.ThreadedWindow")
' (其它情况……)
End Select
' 初始化对象的公用代码。
Call iapt.Initialize(MainApp)
End Sub

注意 可以通过在独立的类型库中定义接口来产生一个只有多线程应用程序知道的 IXxxxApartment 接口。在 ActiveX Exe 工程中,需要设置对该类型库的引用。

保持对线程对象的引用
为了确保能正确地关闭一个多线程应用程序,对于用来创建和控制线程的所有 MultiUse 对象都必须仔细保存对它们的引用情况。

应该清楚地定义对象的存活期目标。举例来说,考虑一个显示窗体的 MultiUse 对象的情况。管理对象存活期的最容易办法是让对象向窗体传递一个 Me 引用;这样窗体就能够保持对象一直存活。如果用户关闭了窗体,窗体的 Unload 事件必然将所有对这个 MultiUse 对象的引用设成 Nothing ,这样对象就能终止并清理它对窗体的引用了。(最好为 MultiUse 对象提供一个友元方法来清理对窗体的引用和所有其它对内部对象引用;窗体的 Unload事件调用这个方法。)

如果控制线程的对象在线程中使用 New 操作符创建了另外的对象,那么应确保清理对这些对象的引用。对于在线程中创建的这些对象,在释放对它们的引用之前线程是无法关闭的。打开的线程要消耗系统资源。

友元方法不能在线程间使用
由于友元属性和方法不是类的公有接口的一部分,因此不能在其它线程中调用它们。对象之间交叉线程的调用只限于声明为 Public 的属性和方法。

重入
如果由于调用 DoEvents 、显示模态窗体,或者对其它线程中对象进行辅助调用而使对象的某个方法被移交控制,那么第二个调用者就可以在第一个调用结束之前进入该方法的代码。如果这个方法使用或修改了属性值或模块级变量,那么这种调用将导致该对象的一种无效内部状态。要防止重入,可以:

避免移交。


为每个方法维护一个模块级布尔标志。在开始一个方法时,它测试这个标志来决定此方法是否正在运行。如果没有,方法就将这个标志设成 True 并继续;否则就产生一个错误。在方法结束或由于任何原因退出时必须仔细地关闭这个标志。


编写重入方法——即不依赖于模块级数据的方法。
异步任务
Visual Basic 没有提供分支执行的途径——就是在一个线程中用某个方法调用一个新的线程,然后立刻在原来的线程中继续处理。通过让原来的方法调用打开一个计时器然后立即返回,可以在测试应用程序中模拟这种行为。当发生计时器事件时,可以将计时器关闭并执行异步处理。这种技术在 “异步回调和事件”中讨论,同时在 Coffee 示例应用程序中有演示(请参阅“创建 ActiveX Exe 部件”)。

使用多线程测试应用程序
要测试单元线程化的部件,必须编译这个多线程测试应用程序,这是因为 Visual Basic 开发环境目前不支持执行多线程。如果有 Visual Studio ,那么可以利用它将测试应用程序编译成带有调试信息的本机代码,这样就可以使用 Visual Studio 的调试程序。
liyang 2000-08-28
  • 打赏
  • 举报
回复
首先,vb中可以实现多线程。是可以使用createthread,不过,如果要传入object类型的话,要使用ole的api,较为复杂。而且,终止线程时,容易出错。

其次,这个问题和使用不使用com没有什么关系。

第三,openprocess是打开一个已存在的进程,而不是开一个线程。当然,也可以完成线程的工作,不过,你好象就要再做个exe了。

第四,应该什么都不动就可以。不知道你的刷卡程序是如何调用的,是定时读取端口还是有可提供事件的驱动程序,如果是事件驱动,你什么都不用动,当刷卡时,会自动调用你的事件中的程序。
如果是使用timer定时读取端口数据,也不需要做额外的工作。因为,timer本身就会开个线程在后台工作。
  • 打赏
  • 举报
回复
你说的:在对某个人进行手工签到的同时,能对其他的人进行自动签到,是做不到出错,还是什么问题?
hmeng 2000-08-27
  • 打赏
  • 举报
回复
非常简单,用COM吧。如果你用的是WIN98/NT,就把它封装在MTS包中,用WIN2000,就封装在COM+ APPLICATION中。具体有很多技巧和注意事项,写起来太长。如果你有兴趣了解。可以给我发email
  • 打赏
  • 举报
回复
可以用Timer来实现
wthai 2000-08-27
  • 打赏
  • 举报
回复
我的工程(会议签到系统)中有两个窗口,一个用来自动签到(通过刷卡),另一个窗口用来手工签到,我想实现下面的操作,在对某个人进行手工签到的同时,能对其他的人进行自动签到,是否要用到VB的多线程编程,如果要用,该对窗口的什么事件编程,希望给出多线程编程这一部分较为详细的代码。我很急,请高手解答,不甚感激!
OUYAN 2000-08-21
  • 打赏
  • 举报
回复
vb下实现多线程编程

用OpenProcess(SYNCHRONIZE, 0, pId)来打开一个线程
用WaitForSingleObject(pHnd, INFINITE)来等待线程结束
用CloseHandle(pHnd)来关闭线程
OUYAN 2000-08-21
  • 打赏
  • 举报
回复
在vb下编制多线程程序
用OpenProcess(SYNCHRONIZE, 0, pId)来打开一个线程
用WaitForSingleObject(pHnd, INFINITE)来等待线程结束
用CloseHandle(pHnd)来关闭线程
shines77 2000-08-21
  • 打赏
  • 举报
回复
谁说 VB 不支持多线程的,API不就行了吗,有现成例子,赌??

这还用赌,VC++能做多线程都是调用API的,VC行,VB也行,只是你不懂而已:)

CreateThread的声明:
Private Declare Function CreateThread Lib "kernel32" Alias "CreateThread" (lpThreadAttributes As SECURITY_ATTRIBUTES, ByVal dwStackSize As Long, lpStartAddress As Long, lpParameter As Any, ByVal dwCreationFlags As Long, lpThreadId As Long) As Long

相关的函数还有:
CreateRemoteThread(), GetCurrentThread(), GetCurrentThreadId(), GetThreadTimes(), TerminateThread() ....等等。
查查API吧,看看书吧,或者MSDN。
david 2000-08-21
  • 打赏
  • 举报
回复
vb不支持多线程编程。
Chen_Lin 2000-08-20
  • 打赏
  • 举报
回复
建议用VC编一个DLL,在DLL中创建一个线程,VB调用即可,然后用VB做其它事情.
shines77 2000-08-20
  • 打赏
  • 举报
回复
是 DoEvents ,也许可以
Mover 2000-08-20
  • 打赏
  • 举报
回复
试试 do Event,也许能解决问题
shines77 2000-08-20
  • 打赏
  • 举报
回复
关注。

7,762

社区成员

发帖
与我相关
我的任务
社区描述
VB 基础类
社区管理员
  • VB基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧