由于设计和调试多线程应用程序非常困难,因此 microsoft 在 com 中创建了“单线程单元”(sta) 概念。visual basic 6 代码始终在 sta 中运行,因而代码只需考虑单线程即可。这样即可彻底避免共享数据或资源所带来的问题,但是同时也意味着,我们必须采取严格的措施才能利用多线程的优势。
public class worker private minner as integer private mouter as integer
public sub new(byval innersize as integer, byval outersize as integer) minner = innersize mouter = outersize end sub
public sub work() dim innerindex as integer dim outerindex as integer
dim value as double
for outerindex = 0 to mouter for innerindex = 0 to minner ' do some cool calculation here value = math.sqrt(cdbl(innerindex - outerindex)) next next end sub
end class 这个类适合在后台线程中运行,并且可以使用以下代码启动:
dim myworker as new worker(10000000, 10) dim backthread as new thread(addressof myworker.work) backthread.start() worker 类中的实例变量可以存放其数据。后台线程可以安全地使用这些变量(minner 和 mouter),还可以确保其他线程不会同时访问这些变量。
为了实现一系列动画点来显示线程的活动,我们将创建一个简单 windows 窗体控件,该控件使用计时器以更改一系列 picturebox 控件中的颜色。
实现方案 我们将在 class library(类库)项目中实现此架构,使其可用于需要运行后台进程的应用程序。
打开 visual studio .net,然后创建一个名为 background 的新 class library(类库)应用程序。由于此库将包含 windows 窗体控件和窗体,因此需要使用 add references(添加引用)对话框引用 system.windows.forms.dll 和 system.windows.drawing.dll。此外,我们可以使用项目的属性对话框在这些项目范围内导入命名空间,如图 6 所示。
图 6:使用项目属性添加项目范围内的命名空间 imports
此操作完成后,就可以开始编码了。让我们先从创建接口开始。
定义接口 在名为 iclient 的项目中添加一个类,并用以下代码替换其代码:
public interface iclient sub start(byval controller as controller) sub display(byval text as string) sub failed(byval e as exception) sub completed(byval cancelled as boolean) end interface
然后添加名为 iworker 的类,并用以下代码替换其代码:
public interface iworker sub initialize(byval controller as icontroller) sub start() end interface
最后添加名为 icontroller 的类,代码如下:
public interface icontroller readonly property running() as boolean sub display(byval text as string) sub setpercent(byval percent as integer) sub failed(byval e as exception) sub completed(byval cancelled as boolean) end interface
private mworker as iworker private mclient as form private mrunning as boolean private mpercent as integer 然后需要声明一些委托。委托是方法的正式指针,而且方法的委托必须具有与方法本身相同的方法签名(参数类型等)。
' 使用客户端初始化 controller public sub new(byval client as iclient) mclient = ctype(client, form) end sub
' 此方法由 ui 调用,因此在 ' ui 线程上运行。此处我们将 ' 启动辅助线程 public sub start(optional byval worker as iworker = nothing) ' 如果辅助线程已经启动,将产生错误 if mrunning then throw new exception("background process already running") end if
' 表明是否仍在运行或是否已请求取消 ' 这将在辅助线程上进行调用,因此 ' 辅助代码可以查看它是否应该正常 ' 退出 private readonly property running() as boolean _ implements icontroller.running get return mrunning end get end property
private mboxes as new arraylist() private mcount as integer
private sub activitybar_load(byval sender as system.object, _ byval e as system.eventargs) handles mybase.load
dim index as integer
if mboxes.count = 0 then for index = 0 to 6 mboxes.add(createbox(index)) next end if mcount = 0
end sub
private function createbox(byval index as integer) as picturebox dim box as new picturebox()
with box setposition(box, index) .borderstyle = borderstyle.fixed3d .parent = me .visible = true end with return box end function
private sub graydisplay() dim index as integer
for index = 0 to 6 ctype(mboxes(index), picturebox).backcolor = me.backcolor next end sub
private sub setposition(byval box as picturebox, byval index as integer) dim left as integer = cint(me.width / 2 - 7 * 14 / 2) dim top as integer = cint(me.height / 2 - 5)
with box .height = 10 .width = 10 .top = top .left = left + index * 14 end with end sub
private sub tmanim_tick(byval sender as system.object, _ byval e as system.eventargs) handles tmanim.tick
ctype(mboxes((mcount + 1) mod 7), picturebox).backcolor = _ color.lightgreen ctype(mboxes(mcount mod 7), picturebox).backcolor = me.backcolor
mcount += 1 if mcount > 6 then mcount = 0 end sub
public sub start() ctype(mboxes(0), picturebox).backcolor = color.lightgreen tmanim.enabled = true end sub
public sub [stop]() tmanim.enabled = false graydisplay() end sub
private sub activitybar_resize(byval sender as object, _ byval e as system.eventargs) handles mybase.resize
dim index as integer
for index = 0 to mboxes.count - 1 setposition(ctype(mboxes(index), picturebox), index) next end sub
private mcontroller as icontroller private minner as integer private mouter as integer
public sub new(byval innersize as integer, byval outersize as integer) minner = innersize mouter = outersize end sub
' 由 controller 调用,以便获取 ' controller 的引用 private sub init(byval controller as icontroller) _ implements iworker.initialize
mcontroller = controller
end sub
private sub work() implements iworker.start dim innerindex as integer dim outerindex as integer
dim value as double
try for outerindex = 0 to mouter if mcontroller.running then mcontroller.display("outer loop " & outerindex & " starting") mcontroller.setpercent(cint(outerindex / mouter * 100))
else ' 它们请求取消 mcontroller.completed(true) exit sub end if
for innerindex = 0 to minner ' 此处进行一些有意思的计算 value = math.sqrt(cdbl(innerindex - outerindex)) next next mcontroller.setpercent(100) mcontroller.completed(false)