Click here to Skip to main content
15,890,512 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm having a threading issue that I'm not sure what to do with.

I'm using Microsoft POS for .NET to initialize some POS hardware. It's horribly slow when used locally and unacceptable on Terminal Services. It takes upwards of 20-40 seconds for three devices. I wanted to multi-thread the initialize of the object on app start up, which was easy. The problem is that one of the objects, the scanner, throws an event when it scans something. The event isn't called from the main thread, since it was created on a BackgroundWorker. I never get the call to the event. If it were my event, it'd be easy to marshal it where I needed, but the problem is that it's a hardware vendor's object that I'm using. Any ideas on how to fix this?

I tried putting the actual wire up to the event on the complete sub of the worker but that didn't help. Here's the basic code:

private void bw_DoWork(object sender, DoWorkEventArgs e)
{ 
  _scanner.Open();
  _scanner.Claim(10000);
  _scanner.DeviceEnabled = true;
}
private void OpenScanner()
{
  if (_scanner == null)
  {
    var queryScanner = (from i in _devices.OfType<DeviceInfo>() where i.LogicalNames.Contains(txtScanner.Text) select i).FirstOrDefault();
    _scanner = (PosCommon)_posExplorer.CreateInstance(queryScanner);
    var bw = new BackgroundWorker();
    bw.DoWork += this.bw_DoWork;
    bw.RunWorkerCompleted += this.bw_RunWorkerCompleted;
    bw.WorkerReportsProgress = false;
    bw.WorkerSupportsCancellation = false;
    bw.RunWorkerAsync();
  }
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    var scan = (Scanner)_scanner;
    scan.AutoDisable = false;
    scan.DecodeData = true;
    scan.DataEventEnabled = true;
    scan.DataEvent += this._Scanner_DataEvent;
    lblGotScanner.Visible = true;
}
\


MORE INFO for andrew and john,

andrew, the _Scanner_DataEvent is never actually fired. the vendor object never fires off the event(or it's being fired somewhere else on some thread. I don't have any control over that unfortunately or this would be easy to fix.

John,
no it's one object to the scanner at one time. It locks the hardware so another object can't do a .claim on it.
the actual scanner object itself is the vendor's code. To use it, you instantiate it, open it, claim it and then enable it. I instantiate it globally to the app now, and tried wiring up the event before I open/claim it in the UI thread, but it didn't help it. It's like once it's opened.claimed on the background worker, all events are there (or no event is getting fired at all)

one thing i thought of but haven't tried because it complicates it a lot more, is to do the threading myself and keep the other thread open. catch the event there, and then marshal my own event back to my UI thread. Assuming the event is actually getting raised from the scanner, i think this would work.
if i remove the background worker it works fine.
Posted
Updated 19-Apr-11 8:03am
v3
Comments
jspano 20-Apr-11 9:00am    
Hmm ok maybe i'm not explaining this well. Sorry. Thanks for the help but either i'm not understanding or not explaining well. The issue is that the event NEVER fires. I can't put stuff in the event, it's not mine to mess with. It's another companies compiled object. If it were mine, it'd be an easy fix. Let me put it in different terms and get rid of the POS concept to simplify it.

Ok say you have a compiled class in a dll that another company did. you have zero control over it. It fires an event. basically this is what i have and this is what i'm doing:

instantiate the object at form level (or app lvl etc)
wire up the event on that object to a function.

in another thread (or backgroundworker) call 2 methods on that global object to claim some hardware that's required for the object to work.

in the above case, my function i have that i wired up to the event never fires. I need some way to take that event and make it fire on my main thread. today i'm going to try to keep the object on a separate thread and have the event over there and marshal it back to my main stuff. I had hoped to not have to go to the trouble of fully threading it but i may have to.

The simplest approach to handling this problem is to perform an InvokeRequired check (assuming WinForms here) and if its required call Invoke.

You would need to perform this within your _Scanner_DataEvent method.

private void _Scanner_DataEvent(object sender, EventArgs e)
{
  // Check InvokeRequired on whatever control/form you like (form is best imo)
  if (this.InvokeRequired)
  {	
    this.Invoke(new DataEventDelegate(_Scanner_DataEvent), new object[] { sender, e });
  }
  else
  {
     // Your existing logic here.
  }
}


For WPF, it would be something like (not 100% sure on the syntax off the top of my head, but that should get you near the solution):

private void _Scanner_DataEvent(object sender, EventArgs e)
{
  // Check InvokeRequired on whatever control/form you like (form is best imo)
  if (!this.Dispatcher.CheckAccess())
  {
    this.Dispatcher.Invoke(DispatcherPriority.Normal, new DataEventDelegate(_Scanner_DataEvent), new object[] { sender, e });
  }
  else
  {
     // Your existing logic here.
  }
}


See MSDN here[^].

This will cause the event to be 're-executed' within the UI thread context.
 
Share this answer
 
v3
Comments
Sergey Alexandrovich Kryukov 19-Apr-11 15:00pm    
Basically correct, but incomplete.
Please see my Answer.
--SA
Andrew Rissing 19-Apr-11 16:54pm    
jspano may correct us, but the issue described was not for cross-thread communication but for interacting with UI elements from a background worker. So, based on my interpretation, the solution is accurate and complete. :)
Sergey Alexandrovich Kryukov 19-Apr-11 16:59pm    
Yes, in this case it is practically complete and can be used, my 5.
My additional info is Dispatcher which can be used in both WPF and Forms and explanations of mechanisms.
--SA
I'm not sure about your design. Is the scanner multi-thread capable? Can multiple scanner objects access different devices at the same time?
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 19-Apr-11 17:03pm    
Good point, my 5. It depends on threading model which can possibly be single-thread apartment state.
Here is when my blocking queue can help to use the mechanism similar to Invoke, please see my Answer. (The design where all use of a scanner is always in one thread is useful in any way.)
--SA
I cannot see what threads should be involved in inter-thread communication. The big difference: if UI thread involved or not. Let's start with UI.

If you need to operate UI from a non-UI thread, you cannot do it through any calls (isn't that natural?). Instead, you have to use methods Invoke or BeginInvoke of System.Threading.Dispatcher or System.Windows.Forms.Control (no matter which control instance; it should be anything included in your Application, for example, your Form).
Dispatcher works for both Forms and WPF.

You will find all the detail on how it works and should be used in my past Solutions:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

If you want to communicate between your custom threads, the situation is more difficult. For a functionality very similar to Invocation on the UI thread, you should use blocking queue.
You can find my implementation here complete with full source code and usage samples:
Simple Blocking Queue for Thread Communication and Inter-thread Invocation[^].

For v.4.0, a similar container already exists (BlockingCollection), but you should use usage patterns shown in my article referenced above.

—SA
 
Share this answer
 
v2
Comments
Espen Harlinn 19-Apr-11 16:12pm    
Very good, my 5 :)
Sergey Alexandrovich Kryukov 19-Apr-11 16:26pm    
Thank you, Espen.
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900