由于設計和調試多線程應用程序非常困難,因此 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)