vb.net - Thread safety problems trying to databind across threads -
i have form datagrids bound bindinglist of data being populated constant stream of data tcpip connection. tcpip connection on thread should constatly loop , when finds enough data create instance of class data , add bindinglist(of data). getting error says "cross-thread operation not valid: control '' accessed thread other thread created on.". stack trace points line adds new data bindinglist(of data)
datalist.insert(0, dataitem)
what strange me keeps going on , populates datagrid properly. not experienced multithreaded programming , of code has been asynchronous. have datalock , mutexes(something learned not sure using correctly). read somewhere using events if newdata event isn't necessary can rid of it. in making code thread safe appreciated.
the binding on form this:
mydatagrid.datasource = mydatareader.datalist
here class launching tread , reading data(sorry know it's lot of code didn't want leave out important):
public class datareader implements inotifypropertychanged #region "properties" dim datamutex mutex dim unparsedmutex mutex dim mip_address string dim convertingdata boolean = false public property ip_address() string return mip_address end set(byval value string) mip_address = value end set end property dim mport integer public property port integer return mport end set(byval value integer) mport = value end set end property private withevents mdatalist bindinglist(of data) public readonly property datalist bindinglist(of data) return mdatalist end end property public event valuechanged eventhandler private event newdataavaiable(byval newdata bindinglist(of data)) private munparseddata string = "" private property unparseddata string return munparseddata end set(byval value string) munparseddata = value end set end property #end region #region "private variables" dim mtcpipclient tcpclient dim mconnected boolean dim mtcpipstream networkstream dim mlasterror string dim mdatathread system.threading.thread private datalock new object #end region #region "constructors" public sub new(byval ipaddress string, byval port integer) datamutex = new mutex(false, "mutexdata") unparsedmutex = new mutex(false, "mutexunparseddata") mip_address = ipaddress mport = port mconnected = false mdatalist = new bindinglist(of data) datalist.raiselistchangedevents = true end sub #end region #region "events" public event propertychanged propertychangedeventhandler implements inotifypropertychanged.propertychanged protected sub onpropertychanged(byval name string) raiseevent propertychanged(me, new propertychangedeventargs(name)) end sub #end region #region "methods" public sub connecttcpip() try if not mtcpipclient nothing andalso mtcpipclient.connected mlasterror = "connection error:already connected" exit sub elseif string.isnullorempty(mip_address) mlasterror = "connection error:no ip address" exit sub end if datalist.clear() unparseddata = string.empty mtcpipclient = new tcpclient() mtcpipclient.connect(mip_address, mport) if mtcpipclient.connected mtcpipclient.receivetimeout = 500 mtcpipclient.sendtimeout = 500 mtcpipclient.lingerstate = new system.net.sockets.lingeroption(false, 0) mtcpipclient.receivebuffersize = 100000 mtcpipclient.sendbuffersize = 100000 mtcpipstream = mtcpipclient.getstream launchdatathread() if mdatathread.isalive mconnected = true else mlasterror = "connection error:unknown reason" exit sub end if catch ex exception messagebox.show(ex.message & ex.stacktrace) end try end sub public sub disconnect() disconnecttcpip() killdatathread() end sub private sub disconnecttcpip() if not mtcpipclient nothing andalso mtcpipclient.connected mtcpipclient.close() mtcpipclient = nothing end if end sub public function isconnected() boolean if mtcpipclient nothing orelse mdatathread nothing return false return mtcpipclient.connected andalso mdatathread.isalive end function public sub launchdatathread() mdatathread = new system.threading.thread(addressof readdata) mdatathread.start() end sub public sub killdatathread() if not mdatathread nothing andalso mdatathread.isalive() mdatathread.abort() end if mdatathread = nothing end sub public sub readdata() try while true dim count short = 0 while not mtcpipclient.connected andalso count <= 5 system.threading.thread.sleep(5000) connecttcpip() count += 1 end while if mtcpipclient.connected = false exit sub dim databuffer(500) byte dim readbytes integer = 0 unparsedmutex.waitone() while mtcpipstream.dataavailable readbytes = mtcpipstream.read(databuffer, 0, 500) unparseddata += system.text.encoding.ascii.getstring(databuffer.take(readbytes).toarray()) loop unparsedmutex.releasemutex() if unparseddata.length > 0 processnewdata() system.threading.thread.sleep(500) end while catch ex exception windows.forms.messagebox.show(ex.message + environment.newline + ex.stacktrace) end try end sub public sub processnewdata() dim datatoprocess string = "" unparsedmutex.waitone() if string.isnullorempty(unparseddata) unparsedmutex.releasemutex() exit sub end if datatoprocess = unparseddata.substring(0, unparseddata.lastindexof("plc") + 3) unparseddata = unparseddata.remove(0, datatoprocess.length) unparsedmutex.releasemutex() if not string.isnullorempty(datatoprocess) dim matches = datatoprocess.split(";"c) if matches.count > 0 dim newdata new bindinglist(of data) try = 0 matches.count - 1 newdata.insert(0, new data(matches(i).value)) next raiseevent newdataavaiable(newdata) catch ex exception windows.forms.messagebox.show(ex.message + environment.newline + ex.stacktrace) end try end if end if end sub private sub onnewdata(byval newdata bindinglist(of data)) handles me.newdataavaiable synclock datalock datamutex.waitone() each dataitem data in newdata datalist.insert(0, dataitem) if datalist.count > 5000 datalist.removeat(5000) next datamutex.releasemutex() end synclock end sub #end region end class
update: bindinglist(of data) never need updated ui thread changing data should mdatathread in datareader class.
look invokerequired, invoke, delegate.
example here, have udp server in background thread(background worker) receiving messageentries , listview1 in main thread. legacy winforms project on net4.0
the following called background thread:
delegate sub setlistviewcallback(byval newmsg messageentry) private sub additemtolistview(byval newmsg messageentry) dim item1 new listviewitem(newmsg.time.tostring(), 0) item1.subitems.add(newmsg.ip.tostring()) if listview1.invokerequired dim d new setlistviewcallback(addressof additemtolistview) me.invoke(d, new object() {newmsg}) else listview1.items.add(item1) end if end sub
edit: checked method bindedlist datasource datagridview , still works. difference conditional if checks if datagridview object requires invoke , in else section put modification of bindedlist.
Comments
Post a Comment