multithreading - VB.NET Trying to modify a generic Invoke method to a generic BeginInvoke method, having unexpected problems -


vb.net 2010, .net 4

hello,

i have been using pretty slick generic invoke method ui updating background threads. forget copied (converted vb.net c#), here is:

public sub invokecontrol(of t control)(byval control t, byval action action(of t))     if control.invokerequired         try             control.invoke(new action(of t, action(of t))(addressof invokecontrol), new object() {control, action})         catch ex exception         end try     else         action(control)     end if end sub 

now, want modify make function returns nothing if no invoke required (or exception thrown) or iasyncresult returned begininvoke if invoke required. here's have:

public function invokecontrol(of t control)(byval control t, byval action action(of t)) iasyncresult     if control.invokerequired         try             return control.begininvoke(new action(of t, action(of t))(addressof invokecontrol), new object() {control, action})         catch ex exception             return nothing         end try     else         action(control)         return nothing     end if end function 

i wanted avoid blocking. problem errors when making calls such this:

invokecontrol(sometextbox, sub(x) x.text = "some text") 

this worked fine original invoke (rather begininvoke) method. "object reference not set instance of object" exception. if put watch on sometextbox, says

sometextbox {text = (text) threw exception of type microsoft.visualstudio.debugger.runtime.crossthreadmessagingexception.} 

it might relevant such invokecontrol calls come within system.timers.timer's elapsed event. interval 500ms, should more long enough ui updates complete (if matters). going on?

thanks in advance help!

edit: more details

here system.timer.timer's elapsed handler:

private sub mastertimer_elapsed(byval sender object, byval e system.timers.elapsedeventargs) handles mastertimer.elapsed     mastertimer.enabled = false     if not mastertimer.interval = my.settings.timingmastertimerinterval         mastertimer.interval = my.settings.timingmastertimerinterval         neweventlogentry("the master timer's interval has been changed " & mastertimer.interval.tostring & " milliseconds.")     end if     invokecontrol(timerpicturebox, sub(x) x.toggle(true))     readfromdevices()     updateindicators()     'this block not executing when error thrown     if mode > runmode.notrunning         updateprocesstime()         updateremainingtime()         updatestatustime()     end if     'this block not executing when error thrown     if mode = runmode.running         checkmillercurrent()         checktolerances()     end if      mastertimer.enabled = true end sub  private sub readfromdevices()     each dev device in devices         try             if dev.gettype.equals(gettype(miller))                 dim devasmiller miller = ctype(dev, miller)                 devasmiller                     if .poweron.enabled .poweron.read()                     if .currentread.enabled .currentread.read()                     if .voltageread.enabled .voltageread.read()                     if .trigger.enabled .trigger.read()                     if .shutter.enabled .shutter.read()                 end             elseif dev.gettype.equals(gettype(substratebiasvoltage))                 dim devassubstratebiasvoltage substratebiasvoltage = ctype(dev, substratebiasvoltage)                 devassubstratebiasvoltage                     if .lambdacurrentread.enabled .lambdacurrentread.read()                     if .lambdavoltageread.enabled .lambdavoltageread.read()                     if .biasresistor.enabled .biasresistor.read()                     if .pinnacle.enabled .pinnacle.read()                 end             else                 if dev.enabled dev.read()             end if         catch ex exception             neweventlogentry("an error occurred while trying read device.", ex, eventlogitem.types.warning)         end try     next end sub  private sub updateindicators()     dim objlock new object     synclock objlock         devices             invokecontrol(emergencystoppicturebox, sub(x digitalpicturebox) x.toggle(mode > runmode.notrunning))              invokecontrol(millercurrentindicator, sub(x) x.text = .miller1.currentread.getparsedvalue.tostring)             invokecontrol(millervoltageindicator, sub(x) x.text = .miller1.voltageread.getparsedvalue.tostring)             .substratebiasvoltage                 invokecontrol(lambdavoltageindicator, sub(x) x.text = .lambdavoltageread.getparsedvalue.tostring)                 invokecontrol(lambdacurrentindicator, sub(x) x.text = .lambdacurrentread.getparsedvalue.tostring)                 invokecontrol(pinnaclevoltageindicator, sub(x) x.text = .pinnacle.getparsedvalue.tostring)                 invokecontrol(pinnaclecurrentindicator, sub(x) x.text = .pinnacle.readcurrent.tostring)             end             invokecontrol(heaterpowerindicator, sub(x) x.text = .heaterpower.getparsedvalue.tostring)             invokecontrol(convectronindicator, sub(x) x.text = .convectron.getparsedvalue.tostring)             if .baratron.getparsedvalue > 200                 invokecontrol(baratronindicator, sub(x) x.text = "off")             else                 invokecontrol(baratronindicator, sub(x) x.text = .baratron.getparsedvalue.tostring)             end if             if .ion.getparsedvalue > 0.01                 invokecontrol(ionindicator, sub(x) x.text = "off")             else                 invokecontrol(ionindicator, sub(x) x.text = .ion.getparsedvalue.tostring)             end if             invokecontrol(argonflowrateindicator, sub(x) x.text = .argonflowrate.getparsedvalue.tostring)             invokecontrol(nitrogenflowrateindicator, sub(x) x.text = .nitrogenflowrate.getparsedvalue.tostring)             invokecontrol(gatevalvepositionindicator, sub(x) x.text = .gatevalveposition.getparsedvalue.tostring)              invokecontrol(roughingpumppoweronindicator, sub(x powerbutton) x.ison = .roughingpumppoweron.value = power.on)              toggleimagelist(.miller1.currentread.imagelist, .miller1.currentread.getparsedvalue > my.settings.minimummillercurrent)             toggleimagelist(.miller1.trigger.imagelist, .miller1.trigger.getparsedvalue = power.on)             toggleimagelist(.heaterpower.imagelist, .heaterpower.value > 0)             .substratebiasvoltage                 toggleimagelist(.lambdavoltageread.imagelist, .lambdavoltageread.getparsedvalue > 0 , .biasresistor.getparsedvalue = biasresistor.lambda)                 toggleimagelist(.pinnacle.imagelist, .pinnacle.getparsedvalue > 10 , .biasresistor.getparsedvalue = biasresistor.pinnacle)             end             toggleimagelist(.argonvalveopen.imagelist, .argonvalveopen.value = valve.open)             toggleimagelist(.nitrogenvalveopen.imagelist, .nitrogenvalveopen.value = valve.open)             toggleimagelist(.roughingpumpvalveopen.imagelist, .roughingpumpvalveopen.value = valve.open)             toggleimagelist(.slowpumpdownvalve.imagelist, .slowpumpdownvalve.value = valve.open)             toggleimagelist(.rotationpoweron.imagelist, .rotationpoweron.value = power.on)             toggleimagelist(.watermonitor1.imagelist, .watermonitor1.value = power.on , .watermonitor2.value = power.on)             toggleimagelist(.gatevalveposition.imagelist, .gatevalveposition.setvalue > 0)         end     end synclock end sub  private sub toggleimagelist(byref imagelist imagelist, byval ison boolean)     each img onoffpicturebox in imagelist         safeinvokecontrol(img, sub(x onoffpicturebox) x.toggle(ison))     next end sub 

i hope that's not tmi, it'll spot going wrong.

also, watch on 1 of textboxes , breakpoints, found error somehow magically thrown after readfromdevices before updateindicators. that, mean breakpoint @ end of readfromdevices shows textboxes haven't thrown error, breakpoint @ beginning of updateindicators (before invokecontrol calls have been made) shows have...

it difficult use debugging catch exception occur on 1 of multiple postmessage calls ui message pump (caused invokecontrol & begininvoke calls). visual studio have hard time breaking on exception. possibly why appears exception "magically" being thrown.

your issue lies not in implementation of invokecontrol in updateindicators method. because of use of with statement , asynchronous ui thread calls this:

with devices     ...     invokecontrol(millercurrentindicator, sub(x) x.text = .miller1.currentread.getparsedvalue.tostring)     ...   end 

because sub(x) code being executed on ui thread posting message on ui thread extremely calling code on current thread have completed before ui thread has been executed.

the problem underlying implementation of visual basic with statement. in essence, compiler creates anonymous local variable with statement gets set nothing @ end with statement.

as example, if have code:

dim p new person p     .name = "james"     .age = 40 end 

the visual basic compiler turns into:

dim p new person dim vb$t_ref$l0 person = p vb$t_ref$l0.name = "james" vb$t_ref$l0.age = 40 vb$t_ref$l0 = nothing 

so, in case, when ui thread code executed anonymous local variable nothing , "object reference not set instance of object" exception.

your code equivalent this:

dim vb$t_ref$l0 = devices dim action = new action(sub(x) x.text = vb$t_ref$l0.miller1.currentread.getparsedvalue.tostring); vb$t_ref$l0 = nothing action(millercurrentindicator); 

by time action called vb$t_ref$l0 variable set nothing , whammo!

the answer not use with statement. bad.


that should answer, there number of other issues in code though should @ too.

your synclock code using local locking variable renders lock useless. don't this:

private sub updateindicators()     dim objlock new object     synclock objlock     devices         ...     end     end synclock end sub 

do instead:

private objlock new object private sub updateindicators()     synclock objlock     devices         ...     end     end synclock end sub 

all of calls invokecontrol in updateindicators method making difficult debug code. reducing of these calls single call should immensely. try instead:

private sub updateindicators()     synclock objlock         invokecontrol(me, addressof updateindicators)     end synclock end sub  private sub updateindicators(byval form controlinvokeform)     devices         emergencystoppicturebox.toggle(mode > runmode.notrunning)         millercurrentindicator.text = .miller1.currentread.getparsedvalue.tostring         ...         toggleimagelist(.gatevalveposition.imagelist, .gatevalveposition.setvalue > 0)     end end sub 

obviously need remove with devices code make these work.

there number of issues following type of code:

if .ion.getparsedvalue > 0.01     invokecontrol(ionindicator, sub(x) x.text = "off") else     invokecontrol(ionindicator, sub(x) x.text = .ion.getparsedvalue.tostring) end if 

the value of .ion.getparsedvalue may have changed between condition being evaluated , else statement being executed. compounded because condition on if statement evaluated on current thread, else statement executed on ui thread delay great. also, if .ion. class not thread-safe exposing potential errors.

do instead:

dim parsedionvalue = .ion.getparsedvalue if parsedionvalue > 0.01     invokecontrol(ionindicator, sub(x) x.text = "off") else     invokecontrol(ionindicator, sub(x) x.text = parsedionvalue.tostring) end if 

(this gets rid of with issue.)

use autoreset = true on mastertimer automatically call enabled = false when event fires avoid (remote) possibility of race conditions.

your code doesn't seem correct in using with devices in updateindicators method, have for each loop in readfromdevices method. devices appears collection, code in updateindicators using devices object if device object. , calling .substratebiasvoltage on devices object. i'm not sure object devices doing.

in toggleimagelist method passing imagelist parameter being passed byref not changing reference imagelist. it's better pass in byval avoid potential bugs.

also, rather doing this:

if dev.gettype.equals(gettype(miller))     dim devasmiller miller = ctype(dev, miller)     devasmiller 

it cleaner this:

dim devasmiller = trycast(dev, miller) if devasmiller isnot nothing     devasmiller 

i hope doesn't seem i'm sinking boot in! helpful.


Comments

Popular posts from this blog

ASP.NET/SQL find the element ID and update database -

jquery - appear modal windows bottom -

c++ - Compiling static TagLib 1.6.3 libraries for Windows -