Accessing objects from different threads

teixeira

Regular
Joined
Apr 5, 2005
Messages
94
Location
LEIRIA-PORTUGAL
Hi

I'm developing an application that reads the serial port to get some data sent by a barcode reader.
To avoid system hang up (because during the barcode reading a lot of treatment has to be done to validate and make some decisions with the data ), i would like to put this in another thread and i could make this, but when i need to access this (from de 2nd thread) data a runtime exception appears, because e can't access objects from cross-threads, it says.

How can i solve this issue?

Thanks in advance, any help will be great,

Tiago Teixeira
 
This type of behavior is by design. It is a protection mechanism put in place for Form threading to protect the contents of controls by ensuring that mutual exclusion takes place. Imagine if any thread could read/write the contents of any control at any time. Without the use of synchronization constructs such as Sempahores and Monitors you would be left with a very unstable application.

You have the right idea. It is a good idea to put long running GUI code in its own thread. The main reason for this is that you do not want to tie up what is known as the "message pump". By default, Form controls are bound to the thread that run their message pump. So, if some long running code is on that thread, the other of the controls on the form are not going to get serviced since they are being blocked.

So, back to the original problem. You've spawned off a new thread in the background that is completely separate from the thread where the control was created. Now, from this background thread, you want to update the control that was created on the main UI thread (as mentioned above). The main two ways to do this are using Invoke, which is synchronous, and BeginInvoke, which is asynchronous. Invoke will cause the calling thread to block while BeginInvoke will allow you to get called back (much like how BeginInvoke works for delegates. However BeginInvoke works in a slightly different manner and you are not required to have a corresponding EndInvoke call).

There are plenty of examples around for both of these, so I won't go into details here. In .NET 2.0 there is a spiffy new type of delegate called MethodInvoker. I use it a lot. This is what it looks like:
C#:
private void MyProcess()
{
    for (int i = 0; i < 10; i++)
    {
        BeginInvoke((MethodInvoker)delegate
        {
            textBox1.Text = Convert.ToString(i);
        });

        Thread.Sleep(500);
    }

}
You could then blast this off in its own thread with:
C#:
Thread thread = new Thread(new ThreadStart(MyProcess));
thread.IsBackground = true;
thread.Start();
Another useful property is the "InvokeRequired" property -- which exists for all form controls. It basically returns true if the current process is running on a thread other than the main UI thread and false if it is not. This is very helpful at times. You can use it like the following:
C#:
private void MyProcess()
{
    for (int i = 0; i < 10; i++)
    {
        if (InvokeRequired)
        {
            BeginInvoke((MethodInvoker)delegate
            {
                // we must marshal our GUI update to the main UI thread
                textBox1.Text = Convert.ToString(i);
            });
        }
        else
        {
            // hey, we're running on the main UI thread -- we can just
            // perform an update as normal
            textBox1.Text = Convert.ToString(i);
        }

        Thread.Sleep(500);
    }

}
(This is a simple example for the sake of demonstration)
 
Thanks a lot for you're explanation, i could solve the problem like that easily, but i have another little question about this:

I created a new thread like this:
Code:
  Thread thread = new Thread(new ThreadStart(BarcodeScannerStart));
            thread.IsBackground = true;
            thread.Start();

here the method declaration for the barcode scanner start:
Code:
        private void BarcodeScannerStart()
        {
            codBarras = new BarcodeScanner();//The class i made to handle barcode reader
            codBarras.OnKenNumberReceived += new BarcodeScanner.KenNumberEventHandler(codBarras_OnKenNumberReceived);//I regist the event and each time barcode receives data it writes that data into my textBox1
        }

Now e put the value returned by barcode reader in my textbox1 as you suggested
Code:
        void codBarras_OnKenNumberReceived(string kennumber)
        {
            BeginInvoke((MethodInvoker)delegate{textBox1.Text += kennumber + Environment.NewLine;});

            //Here i wanna kill the BarcodeReaders Thread. How can i do it safely?


        }

Thanks for all Gill Bates, you helped me a lot

Best Regards,
Tiago Teixeira
 
Here's an approach that I use for this type of situation and it has never led me wrong.

1) You make Start / Stop methods available to the caller

2) You encapsulate the threaded operation inside the class itself, instead of placing the burden on the caller

3) You make sure that everything inside your class is completely thread safe (including firing delegates and raising events).

I whipped up an example of how I would implement your class. You would simply begin the scanning operation like this:
C#:
BarcodeScanner scanner = new BarcodeScanner();
scanner.OnKenNumberReceived += new BarcodeScanner.KenNumberReceived(KenNumberReceived);
scanner.Start();
The event handler would look like:
C#:
private void KenNumberReceived(string barcodeNumber)
{
    Console.WriteLine("RECEIVED: " + barcodeNumber);
}
And, when you wanted to stop the scanning operation, you would simply need to call the "Stop" method like this:
C#:
scanner.Stop();
Using this approach the BarcodeScanner class would look like this:
C#:
public class BarcodeScanner
{
    private readonly object _scanLock = new object();

    // notice that the _scanning variable is declared with the volatile keyword
    // this will ensure that the contents of this variable are not cached and
    // that we are always dealing with a fresh data value
    private volatile bool _scanning;

    private Thread _scanThread = null;
                    
    public delegate void KenNumberReceived(string barcodeNumber);


    #region synchronized event
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Syncrhonized event
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~        

    // create an object to lock on in order to provide synchronized access to the event
    private readonly object _eventLock = new object();

    // the actual underlying delegate for the event
    private KenNumberReceived _onKenNumberReceived;

    // public access to the underlying delegate for the event
    public event KenNumberReceived OnKenNumberReceived
    {
        add
        {
            lock (_eventLock)
            {
                _onKenNumberReceived += value;
            }
        }
        remove
        {
            lock (_eventLock)
            {
                _onKenNumberReceived -= value;
            }
        }
    }

    // private method to actually fire the event
    private void FireEvent(string barcodeNumber)
    {
        KenNumberReceived handler;

        lock (_eventLock)
        {
            handler = _onKenNumberReceived;                
        }

        if (handler != null)
            handler(barcodeNumber);
    }
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~        
    #endregion
   
    public void Start()
    {
        if (_scanThread == null)
        {
            _scanning = true;

            // spawn a new thread to do the scanning
            _scanThread = new Thread(new ThreadStart(Scan));
            _scanThread.IsBackground = true;
            _scanThread.Start();
        }
    }

    private void Scan()
    {
        int counter = 0;

        // we do not need to lock on the _scanning boolean variable
        // since it is able to be read from and written to atomically
        while (_scanning)
        {
            // do some sort of long running operation here
            Console.WriteLine("Scanning...");

            // simulate a new bar code being scanned
            FireEvent(Convert.ToString(counter++));

            // if the above operation blocks or is long running
            // you will not need to sleep.  I'll put this in here
            // just to break the process up a little bit.
            Thread.Sleep(500);
        }
    }

    public void Stop()
    {
        _scanning = false;

        _scanThread.Abort();

        _scanThread = null;

        Console.WriteLine("Done scanning...");
    }
}
The comments should explain everything but the most important things to take note of are the methods I use to provide synchronized access to the event delegate and how I utilize the volatile keyword to control the scanning operation. An important thing to note is that a boolean in C# does not need to be locked as the CLR allows you to read and write booleans in an atomic operation. This means that these operations will always execute in a single thread context and you do not have to worry about a critical section violation.
 
I should also point out an important note about the FireEvent method. You will notice that it looks like this:
C#:
// private method to actually fire the event
private void FireEvent(string barcodeNumber)
{
    KenNumberReceived handler;

    lock (_eventLock)
    {
        handler = _onKenNumberReceived;                
    }

    if (handler != null)
        handler(barcodeNumber);
}
It is important that you only lock on _eventLock when needed. If you compare the above code to this version of the FireEvent method...
C#:
// private method to actually fire the event
private void FireEvent(string barcodeNumber)
{
    lock (_eventLock)
    {
        if (_onKenNumberReceived != null)
            _onKenNumberReceived(barcodeNumber);
    }
}
...you will notice a subtle difference. The difference is that we are actually firing the event delegate from within the lock. This is very bad. All of the underlying methods for the delegate are going to be executed before the lock is released. This is going to cause blocks throughout our class where we do not want them. I can not even begin to count how many times I have seen people utilize the second approach. If you use the first implementation you will never need to worry about problems when firing events from within a threaded process.
 
Back
Top