分享
 
 
 

Starting a separate code thread

王朝vb·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Starting a

Separate Code Thread

in Visual Basic?/H3>

by Rick Meyer

Table

of

Contents

1. Overview

2. What is a separate code thread?

3. Why/When to have a separate thread.

4. The tool for separate threads in Visual Basic.

5. The problem starting the server's execution.

6. RaiseEvent is a one way street.

7. Reentrancy considerations.

8. The timer examples in the book.

9. A solution for starting the server's execution.

10. A bit about marshaling.

11. The Mini-Database demo project.

Overview

In Visual Basic it is possible to obtain one or more additional separate code threads to operate a single program. The threads are stable and secure and communication between them is provided for intrinsicly. There is no need to hack the language with unpredictable API's.

This page focuses on how that is done and demonstrates the problem you may have (or not be aware of) in starting these threads running.

Client and server are object oriented programming (OOP) terms. To simplify matters in the ensuing discussion, the client will be a standard EXE. The server will be an ActiveX component object instantiated by the client. The client sends information and requests to the server object. The server responds when the job is done by raising an event in the client and usually returning some processed information to the client.

What is a separate code thread?

A thread is a list of assembly level instructions that the CPU (central processing unit) of your computer executes one after another, one at a time, all the way until a program ends and releases the thread. The Windows?programs that you can switch between when you click on the taskbar at the bottom of the screen are operating on separate threads. The amazing thing is that not only can you switch from one program to another, but they can all run at once!

This is possible because the CPU can switch code threads. It stops executing one thread, saves all the information about that thread, loads all the information about the next thread in line, and then starts executing that set of instructions. Countless thread code switches are happening right now in your computer every second as you read this.

So technically the programs on your taskbar are not all running at once, but the switching normally happens so fast that it seems like they are.

A switch occurs whenever the CPU gets a break from the program holding the presently executing thread. In Visual Basic these breaks are provided automatically when waiting for some type of input (a key press or a mouse click). If your code performs many instructions without a break, it may monopolize the CPU so that other programs can't get their share of CPU time. In such a case, it is up to you as a good programmer to be courteous and judiciously allow breaks with the DoEvents statement.

Why/When to have a separate thread:

If you play music on your computer while you type an email reply, or if you keep more than one web browser window open at the same time to download one page while you read another, then you are aware of the benefits of separate code threads.

In these instances, the operating system provides separate threads automatically, and the applications really don't have to work together as client and server.

When could we take advantage of separate code threads in a single program?

?/FONT>

To synchronize input of data with processing data. An example would be allowing an user to complete a second form, while the first is being processing.

?/FONT>

To synchronize complex computations where separate, alternating processing may allow for more rapid completion. i.e. - The result of processing on one thread arrives in time to be used on the other.

?/FONT>

To resolve or eliminate reentrancy quirks and conflicts that arise in a single thread when it overlaps itself and generally performs operations in an order not anticipated. Chasing these overlaps with flags and exits ends up becoming just plain bad programming.

?/FONT>

To provide a model of a remote server during development. In network or internet settings, it is possible to instantiate a remote server object for a local client program. Here again a separate thread is provided automatically because, of course, two machines can not run in the same thread. However, the code for such a remote instantiation is usually subtly if not significantly different than the code for a local server. A separately threaded local program will provide a precise model to enable the programmer to think "remotely."

The tool for separate threads in Visual Basic:

Of course such a discussion would be moot if there were no vehicle for implementation. Fortunately, Visual Basic has provided us such a tool through ActiveX, and specifically the ActiveX EXE type component. It is there specifically to give us a separate thread - if you only need a same thread library, then choose an ActiveX DLL as your project.

In Visual Basic:

Same thread = In process = ActiveX DLL

Separate thread = Out of process = ActiveX EXE

ActiveX has been designed by Microsoft to nudge Visual Basic programmers toward object oriented programming. It is through the instantiation of a required public class that ActiveX is able to pipe us into the safe haven of the COM standard and provide us with secure conversations between separate threads. You will also be happy to learn that the App Object has a .ThreadID property which will be your compass for exploration.

It is the ActiveX EXE we shall explore for the separate code thread it provides. Visual Basic has the ability to allow for multi-threads of separately threaded EXE servers (additional threads for each instantiation) through unattended execution compilation options. The examples herein only deal with single, separate threads of execution.

Being a single, separate thread does not mean that it can not have more than one client instantiating and calling it. When a component has one thread of execution, code for only one object executes at a time. The Automation feature of the Component Object Model (COM) deals with this situation by serializing requests. That is, the requests are queued and processed one at a time until all have been completed. It is limited because if control is yielded, the next queued call is allowed entry and if unanticipated, reentrancy problems may occur.

In ActiveX components, control is yielded with:

1. DoEvents

2. Calling an object out of process

3. Raising an event in an object out of process Another advantage of having a single, separate thread on which all instances of a server object (instantiations by more than one client included) will run is that globally defined variables in the server (declared Public in a code module) will be visible to all instances of the server. Such global variables in multi-threaded servers are not supported in Visual Basic. (It's hard to do because it requires synchronization of the multi-threads.)

The problem starting the server's execution:

Simply calling a public method of a server running on a separate thread will start the server executing code. The problem is that the calling client will wait at that instruction for the return from the server. This will happen even if the call is to a Sub in the server (i.e. expecting no return value as with a function).

To illustrate:

Example One:

'==============================================

'The Server Code: compiled as an ActiveX EXE

' Class name: threadServer

'==============================================

Option Explicit

Event done(id&)

Public Sub waitAwhile()

Dim j#, k#

For j = 1 To 300000

k = j

DoEvents

Next

RaiseEvent done(App.ThreadID)

End Sub

'==============================================

'The Client Code run from the VB IDE

'==============================================

Option Explicit

Private WithEvents ts1 As threadServer

Private Sub Form_Load()

AutoRedraw = True

Set ts1 = New threadServer

End Sub

Private Sub Form_Click()

Cls

ts1.waitAwhile

Print "Client done at: " & Str$(Timer) & _

" on thread: " & Str$(App.ThreadID)

End Sub

Private Sub ts1_Done(id&)

Print "Server done at: " & Str$(Timer) & _

" on thread: " & Str$(id)

End Sub

Private Sub Form_Unload(Cancel As Integer)

Set ts1 = Nothing

End Sub

The output proves two things:

There are, in fact, separate threads

The client waits until the server is finished.

The Output:

The form is clicked and ts1.waitAwhile starts the server running. When the server is done it raises an event in the client to say that it has finished. The client may finally go about its business and posts its message. Note that DoEvents is placed in the server to give this experimental attempt all possible chances at success.

The hoped for benefit of separate threads is that execution would proceed for both threads. If one thread has to mark time waiting for the other to finish, then there is little benefit from separate threads.

RaiseEvent is a one way street:

If the client could raise an event in our server in the first place, instead of making a call, our problem would be solved. No waiting occurs when you raise an event.

Unfortunately, Visual Basic has provided for events that go only in one direction - from the server to the client (commonly to notify the client that the job is done). Apparently Microsoft's assumption is that if a client wants to contact the server then it should make a call.

Reentrancy considerations:

Note that we could force the client to budge with an event from the server, but that method is bound for reentrancy trouble. The technique also requires double communication through the COM barrier, which is not a strategy for optimal performance.

Reentrancy occurs when a thread of code is interrupted by an event such that the thread reenters itself at a different point in the program. Hopefully the code executed is a little snipit of your program that can be executed without adversely effecting the thread that was interrupted. If an interrupting thread changes module-level data the first thread was using, the result may be unfortunate.

In the situation described, the client would have one portion of its thread waiting for the return of the server call, another executing code as the result of the server originated event, and finallly a third when the event arrives from the server indicating that the job is done.

Note that the third reason stated above to have a separate thread in the first place is to avoid reentrancy problems.

The timer examples in the book:

There are examples and discussion in Books on Line in Help and a rather bewilderingly complete project in VB\samples\CompTool\ActvComp\coffee. You could spend considerable time attempting to decipher the heavily commented code covering a maze of files.

If you study it, at some point you come to realize that the only thing they are doing at any time to demonstrate a separate thread is to enable an API timer. Unless visual demonstration of apartment threading is your goal, it is a colossal waste of effort. Well not quite ...

It becomes apparent that somehow enabling a timer is the key.

A solution for starting the server's execution:

A more obvious method for initiating server action would be to simply enable a timer that periodically checks for data (deposited by the client) to act upon. But if you, like I, prefer avoiding the attendant overhead of a continually running timer, I believe you will find the following solution appealing.

In the server's Sub called from the client:

Transfer data passed as parameters to the server's module level variables.

Enable a timer in the server whose event will start executing server code.

Exit immediately

In the timer event, disable the timer immediately before firing the event to start the server's execution. Note that the timer interval may be set to 1 millisecond, plenty of time for the client's call to return from the server. If you generally try to avoid timers in applications, I believe you will find this to be the exception. The timer is enabled for a single interval and only for the minimum of 1 millisecond for each call to the server.

To illustrate:

Example Two:

'==============================================

'The Server Code: compiled as an ActiveX EXE

' Class name: threadServer

'==============================================

Option Explicit

Event done(id&)

Dim a&, b&

Dim WithEvents f1 As Form1

Private Sub Class_Initialize()

Set f1 = New Form1

End Sub

Public Sub theCall(ByVal n1&, ByVal n2&)

a = n1: b = n2

f1.Timer1.Enabled = True

End Sub

Private Sub f1_TimerNotify()

processInfo

End Sub

Private Sub processInfo()

Dim j#, t&

For j = 1 To 3000000

t = a + b

Next

RaiseEvent done(t)

End Sub

Private Sub Class_Terminate()

Unload f1

Set f1 = Nothing

End Sub

'==============================================

'The Form with a Timer on it (in the server)

'==============================================

Option Explicit

Event TimerNotify()

Private Sub Form_Load()

Me.Timer1.Enabled = False

Me.Timer1.Interval = 1

End Sub

Private Sub Timer1_Timer()

Me.Timer1.Enabled = False

RaiseEvent TimerNotify

End Sub

'==============================================

'The Client Code run from the VB IDE

'==============================================

Option Explicit

Private WithEvents ts1 As threadServer

Private Sub Form_Load()

AutoRedraw = True

Set ts1 = New threadServer

End Sub

Private Sub Form_Click()

Cls

ts1.theCall 6, 7

Print "Client done at: " & Str$(Timer) & _

" requesting: 6 + 7"

End Sub

Private Sub ts1_Done(ans&)

Print "Server done at: " & Str$(Timer) & _

" answering: " & Str$(ans)

End Sub

Private Sub Form_Unload(Cancel As Integer)

Set ts1 = Nothing

End Sub

The output demonstrates execution of both threads.

On the same machine, the ultimate execution of separate threads is manifested by sharing the CPU through thread switching. Therefore, this output has not provided us with proof of simulltaneous execution. (It has proven that many instructions can be executed in a millisecond.)

Such output would require additional programming in both client and server objects for synchronization of their tasks (presumably with additional events and/or calls) However the technique of initiating a second thread remains intact and it may be applied without additional synchronization code if your server object is, in fact, instantiated remotely.

A bit about marshaling:

For out of process calls (and events raised) a proxy service called marshaling is required to pass parameters. Whatever you pass is always passed "by value" via marshaling. A reference (which may be specified) forces values to also be marshaled back to the caller (also by value).

Therefore pass everything ByVal. Arrays should be made Variants so that they may be passed ByVal. This applies to arrays of any type since none (except Variant) may be passed ByVal in Visual Basic.

Example ByVal array passing code:

'===============================================

'Code in the standard EXE client

'===============================================

Dim dbe As myServerClass

Dim myArray(6) As Variant

Private Sub SendToClass()

dbe.setClassData myArray()

End Sub

'===============================================

'Code in the server class

'===============================================

Dim wholeClsVis(6) As Variant

Public Sub setClassData(ByVal a As Variant)

Dim j%

For j = 0 to 6

wholeClsVis(j) = a(j)

Next

End Sub

The Mini-Database demo project:

To demonstrate the techniques advanced on this page in a more robust functioning application, download the Mini-Database Project (26k) and see the complete commented source code.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有