Async Invoke And Threads

mafrosis

Well-known member
Joined
Jun 5, 2006
Messages
88
Location
UK
Programming Experience
5-10
Hey all, I posted a vague question in the Database forum yesterday about a problem I had with Access file locking. I start another thread to do some work exporting to Access, but when the thread completes I need to copy the Access file over our network - and then delete it.

This was a problem because while the worker thread was alive, the Access file was locked (the goddamn .ldb file). To get around this, I used some Async calls (BeginInvoke, EndInvoke) to execute on the main thread, thus causing the worker thread to end and release the Access lock. I could then delete it..

Now, Ive never really done the whole Async thing before with Begin/End, and the msdn docs are unintuitive to say the least. Ive made a small example below so can someone tell me if im doing this the correct way? The code is complete so can be copied into a project and run.

Thanks all!! Much appreciated.

edit: I dont why but sometimes the BeginInvoke() throws an exception - only sometimes tho! This doesnt happen in my real application!!

VB.NET:
Expand Collapse Copy
[COLOR=blue][FONT=&quot]Public[/FONT][/COLOR][FONT=&quot] [COLOR=blue]Class[/COLOR] Form[/FONT]
  
  [FONT=&quot]       <System.STAThread()> _[/FONT]
  [FONT=&quot]       [COLOR=blue]Public[/COLOR] [COLOR=blue]Shared[/COLOR] [COLOR=blue]Sub[/COLOR] Main()[/FONT]
  [FONT=&quot]              Application.Run([COLOR=blue]New[/COLOR] Form())[/FONT]
  [FONT=&quot]       [COLOR=blue]End[/COLOR] [COLOR=blue]Sub[/COLOR][/FONT]
  
  
  [FONT=&quot]       [COLOR=blue]Private[/COLOR] [COLOR=blue]WithEvents[/COLOR] lblStatus [COLOR=blue]As[/COLOR] [COLOR=blue]New[/COLOR] Label()[/FONT]
  [FONT=&quot]       [COLOR=blue]Private[/COLOR] [COLOR=blue]WithEvents[/COLOR] butDoStuff [COLOR=blue]As[/COLOR] [COLOR=blue]New[/COLOR] Button()[/FONT]
  
  [FONT=&quot]       [COLOR=blue]Private[/COLOR] [COLOR=blue]WithEvents[/COLOR] AsyncMan [COLOR=blue]As[/COLOR] [COLOR=blue]New[/COLOR] AsyncManager([COLOR=blue]Me[/COLOR])[/FONT]
  
  [FONT=&quot]       [COLOR=blue]Private[/COLOR] [COLOR=blue]Sub[/COLOR] Form_Load([COLOR=blue]ByVal[/COLOR] sender [COLOR=blue]As[/COLOR] System.Object, [COLOR=blue]ByVal[/COLOR] e [COLOR=blue]As[/COLOR] System.EventArgs) [COLOR=blue]Handles[/COLOR] [COLOR=blue]MyBase[/COLOR].Load[/FONT]
  [FONT=&quot]              lblStatus.Location = [COLOR=blue]New[/COLOR] Point(16, 24)[/FONT]
  [FONT=&quot]              lblStatus.Text = [COLOR=maroon]"Waiting"[/COLOR][/FONT]
  [FONT=&quot]              butDoStuff.Location = [COLOR=blue]New[/COLOR] Point(16, 56)[/FONT]
  [FONT=&quot]              butDoStuff.Size = [COLOR=blue]New[/COLOR] Size(160, 40)[/FONT]
  [FONT=&quot]              butDoStuff.Text = [COLOR=maroon]"Do Large Query"[/COLOR][/FONT]
  [FONT=&quot]              [COLOR=blue]Me[/COLOR].Controls.Add(lblStatus)[/FONT]
  [FONT=&quot]              [COLOR=blue]Me[/COLOR].Controls.Add(butDoStuff)[/FONT]
  [FONT=&quot]       [COLOR=blue]End[/COLOR] [COLOR=blue]Sub[/COLOR][/FONT]
  
  
  [FONT=&quot]       [COLOR=blue]Private[/COLOR] [COLOR=blue]Sub[/COLOR] butDoStuff_Click([COLOR=blue]ByVal[/COLOR] sender [COLOR=blue]As[/COLOR] System.Object, [COLOR=blue]ByVal[/COLOR] e [COLOR=blue]As[/COLOR] System.EventArgs) [COLOR=blue]Handles[/COLOR] butDoStuff.Click[/FONT]
  [FONT=&quot]              lblStatus.Text = [COLOR=maroon]"Started"[/COLOR][/FONT]
  
  [FONT=&quot]              [COLOR=green]'start doing a query[/COLOR][/FONT]
  [FONT=&quot]              AsyncMan.StartAsync()[/FONT]
  [FONT=&quot]       [COLOR=blue]End[/COLOR] [COLOR=blue]Sub[/COLOR][/FONT]
  
  [FONT=&quot]       [COLOR=blue]Private[/COLOR] [COLOR=blue]Sub[/COLOR] AsyncFinished() [COLOR=blue]Handles[/COLOR] AsyncMan.OnAsyncComplete[/FONT]
  [FONT=&quot]              lblStatus.Text = [COLOR=maroon]"Finished"[/COLOR][/FONT]
  [FONT=&quot]       [COLOR=blue]End[/COLOR] [COLOR=blue]Sub[/COLOR][/FONT]
  
  [COLOR=blue][FONT=&quot]End[/FONT][/COLOR][FONT=&quot] [COLOR=blue]Class[/COLOR][/FONT]
  


[COLOR=blue][FONT=&quot]Public[/FONT][/COLOR][FONT=&quot] [COLOR=blue]Class[/COLOR] AsyncManager[/FONT]
   
   [FONT=&quot]       [COLOR=blue]Private[/COLOR] [COLOR=blue]WithEvents[/COLOR] dba [COLOR=blue]As[/COLOR] [COLOR=blue]New[/COLOR] DBAccess()[/FONT]
   
   [FONT=&quot]       [COLOR=blue]Private[/COLOR] [COLOR=blue]Delegate[/COLOR] [COLOR=blue]Sub[/COLOR] LargeQueryCompleteDelegate[/FONT]
   [FONT=&quot]       [COLOR=blue]Private[/COLOR] LargeQueryCompleteEvent [COLOR=blue]As[/COLOR] [COLOR=blue]New[/COLOR] LargeQueryCompleteDelegate([COLOR=blue]AddressOf[/COLOR] AsyncComplete)[/FONT]
   
   [FONT=&quot]       [COLOR=blue]Private[/COLOR] AsyncResult [COLOR=blue]As[/COLOR] IAsyncResult             [COLOR=green]'result for async thread call[/COLOR][/FONT]
   
   [FONT=&quot]       [COLOR=blue]Public[/COLOR] [COLOR=blue]Event[/COLOR] OnAsyncComplete[/FONT]
   
   
   [FONT=&quot]       [COLOR=blue]Private[/COLOR] Parent [COLOR=blue]As[/COLOR] Control  [COLOR=green]'parent control that runs in UI thread[/COLOR][/FONT]
   
   [FONT=&quot]       [COLOR=blue]Public[/COLOR] [COLOR=blue]Sub[/COLOR] [COLOR=blue]New[/COLOR]([COLOR=blue]ByRef[/COLOR]  ParentForm [COLOR=blue]As[/COLOR] Control)[/FONT]
   [FONT=&quot]              [COLOR=blue]Me[/COLOR].Parent = ParentForm[/FONT]
   [FONT=&quot]       [COLOR=blue]End[/COLOR] [COLOR=blue]Sub[/COLOR][/FONT]
   
   
   [FONT=&quot]       [COLOR=blue]Public[/COLOR] [COLOR=blue]Sub[/COLOR] StartAsync()[/FONT]
   [FONT=&quot]              [COLOR=blue]Dim[/COLOR] t [COLOR=blue]As[/COLOR] [COLOR=blue]New[/COLOR] Threading.Thread([COLOR=blue]AddressOf[/COLOR] dba.LargeQuery)[/FONT]
   [FONT=&quot]              t.Start()[/FONT]
   [FONT=&quot]       [COLOR=blue]End[/COLOR] [COLOR=blue]Sub[/COLOR][/FONT]
   
   
   [FONT=&quot]       [COLOR=blue]Public[/COLOR] [COLOR=blue]Sub[/COLOR] AsyncFinished() [COLOR=blue]Handles[/COLOR] dba.OnQueryFinished[/FONT]
   [FONT=&quot]              [COLOR=green]'***** INVOKE EVENT ON MAIN THREAD *****'[/COLOR][/FONT]
   [FONT=&quot]              [COLOR=green]'***** OTHER THREAD WILL FINISH *****'[/COLOR][/FONT]
   
   [FONT=&quot]              AsyncResult = Parent.BeginInvoke(LargeQueryCompleteEvent)[/FONT]
   [FONT=&quot]       [COLOR=blue]End[/COLOR] [COLOR=blue]Sub[/COLOR][/FONT]
   
   [FONT=&quot]       [COLOR=blue]Public[/COLOR] [COLOR=blue]Sub[/COLOR] AsyncComplete()[/FONT]
   [FONT=&quot]              [COLOR=green]'***** MOVE ACCESS FILE TO SHARED DRIVE *****'[/COLOR][/FONT]
   [FONT=&quot]              [COLOR=green]'***** ACCESS FILE DELETED NOW *****'[/COLOR][/FONT]
   
   [FONT=&quot]              Parent.EndInvoke([COLOR=blue]Me[/COLOR].AsyncResult)[/FONT]
   
   [FONT=&quot]              [COLOR=blue]RaiseEvent[/COLOR] OnAsyncComplete[/FONT]
   [FONT=&quot]       [COLOR=blue]End[/COLOR] [COLOR=blue]Sub[/COLOR][/FONT]
   
   [COLOR=blue][FONT=&quot]End[/FONT][/COLOR][FONT=&quot] [COLOR=blue]Class[/COLOR][/FONT]
   
   
   
   [COLOR=blue][FONT=&quot]Public[/FONT][/COLOR][FONT=&quot] [COLOR=blue]Class[/COLOR] DBAccess[/FONT]
   
   [FONT=&quot]       [COLOR=blue]Public[/COLOR] [COLOR=blue]Event[/COLOR] OnQueryFinished[/FONT]
   
   [FONT=&quot]       [COLOR=blue]Public[/COLOR] [COLOR=blue]Sub[/COLOR] LargeQuery()[/FONT]
   [FONT=&quot]              Threading.Thread.Sleep(3000)[/FONT]
   
   [FONT=&quot]              [COLOR=green]'***** ACCESS FILE CREATED HERE *****'[/COLOR][/FONT]
   [FONT=&quot]              [COLOR=green]'***** ACCESS FILE LOCK IS HELD BY THIS THREAD *****'[/COLOR][/FONT]
   
   [FONT=&quot]              [COLOR=blue]RaiseEvent[/COLOR] OnQueryFinished[/FONT]
   [FONT=&quot]       [COLOR=blue]End[/COLOR] [COLOR=blue]Sub[/COLOR][/FONT]
   
   [COLOR=blue][FONT=&quot]End[/FONT][/COLOR][FONT=&quot] [COLOR=blue]Class[/COLOR][/FONT]
 
I would think that you'd just be better off using a BackgroundWorker. Then you can simply handle its RunWorkerCompleted event to know when the operation has finished and you can then delete the MDB file. Also, you probably could have used a FileSystemWatcher to detect when the LDB file was deleted to indicate that the MDB file was accessible.
 
Ok no worries, I read about the BackgroundWorker and it sounds about right - but I assume it does internally what im trying to do here (?).

My question is really about whether my implementation is correct - or if you like, is this how the BackgroundWorker does it?

I like to understand the guts of it before I start using all these helper classes.. :D
 
With your code, mafrosis, I always get 'target of invokation exception'. I found this solution in documentation for IAsyncResult Interface:
VB.NET:
Expand Collapse Copy
Public Sub AsyncFinished() Handles dba.OnQueryFinished
'***** INVOKE EVENT ON MAIN THREAD *****'
'***** OTHER THREAD WILL FINISH *****'
AsyncResult = Parent.BeginInvoke(LargeQueryCompleteEvent)
AsyncResult.AsyncWaitHandle.WaitOne() 'waits for completion (still on DBA thread)
Parent.EndInvoke(Me.AsyncResult)
End Sub
 
Public Sub AsyncComplete()
'***** MOVE ACCESS FILE TO SHARED DRIVE *****'
'***** ACCESS FILE DELETED NOW *****'
RaiseEvent OnAsyncComplete()
End Sub
You could also look at the AsyncCallback, see one example here http://www.vbdotnetforums.com/showthread.php?t=11665
 
Back
Top