Page 1 of 1

Cross Thread Communication in C#

#1 PsychoCoder  Icon User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1634
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Post icon  Posted 18 October 2007 - 05:37 AM

*
POPULAR

Welcome to the first in a series of tutorials on Multi-Threading in C#. Before we even think about looking at any code, I think first, since this is the intro tutorial, we need to discuss what multi-threading is, and how it can benefit your application. We will also discuss some short falls in multi-threading capabilities.

What is multi-threading?

If you were to ask 100 programmers the question "What is a thread?", you'd leave with 100 different answers. To me a Thread is a tool that allows concurrent, independent processes to run parallel to one another, without affecting each other, and without having to wait for the other to complete before it can execute. It used to be that an application had but a single thread, which meant a process was made operate sequentially from beginning to end, with no variations. With the introduction of multi-threading you can run step 1 in its own thread, while running step 2 in a separate thread. While that may not always be a good idea, especially if step 1 requires results from step 3, so that was just an example of how multi-threading works.

Why multi-thread?

Multi-threading allows your application to run more efficiently, multi-threading affords you the opportunity to run resource intensive processes in the background, allowing your interface to remain usable. If you were to run your entire application in a single thread, you would notice at times that your user interface would, for all intents and purposes, be unusable, until the current process completed. Though there are numerous advantages to creating multi-threaded applications, there are also safety concerns. The main concern when creating multi-threaded applications is the possibility of multiple threads manipulating the same data, or space in memory, at the same time, this is known as a Race Condition.

Debugging a Race Condition can be difficult, as the condition can be sporadic and are often hid from the developer, and may not appear until the application is ran repeatedly. As long as you write thread-safe code you can avoid race conditions. Determining if code is thread-safe usually isnt an easy task, but there are several items that will reveal unsafe code, such as instantiating a class that contains static variables and indirectly accessing shared memory with the use of pointers or handles.

Limiting or avoiding the sharing of data across multiple threads is a way of ensuring you're code is thread-safe, but there are times when this is necessary. Say you have a large process running in the background, and you're wanting to keep the user updated on the progress of said process. Since the controls on the form are only accessible by the thread in which they were created, and sharing of data between threads is dangerous, for the lack of a better term, what do you do?

In this situation you would use a Delegate to marshal the call onto the thread that created the controls, using the Invoke Method. There are four methods that you can call from any thread and still have your code considered thread-safe:Now that we have a better understand of what a thread is, and what multi-threading is, lets look at how we would go about using a Delegate to marshal a call onto the UI thread with the Invoke Method. First we, as usual, need references to our Namespaces. The Namespaces we will be using are:
using System;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Collections.ObjectModel;



Now we need our variables that will be referenced throughout our class. One of the variables is an ISynchronizeInvoke which allows us to synchronously or asynchronously execute our delegate. So here are the variables I use:

#region Class variables
//instance of our delegate
private static ProcessStatus _status;
//our thread object
private static Thread _thread;
//our ISynchronizeInvoke object
private static ISynchronizeInvoke _synch;
//our list we'll be adding to
private static Collection<string> _listItems = new Collection<string>();
#endregion



Next, we need a Delegate which we will use to marshal our call to the UI Thread, so we can display the progress of our process to the user on the UI:

//our delegate, which will be used to marshal our call
//to the UI thread for updating the UI
public delegate void ProcessStatus(string Message,int status);



Next, as with any class we need Constructors. A constructor is a member function with the same name as its class, and are used to create, and can initialize, objects of their class type. Constructors can be empty, the can accept no parameters and set variables to a desired state when the class is initialized, they can also accept parameters for initializing variables global to the class to a certain value:

#region Constructors
/// <summary>
/// Constructor using overloading
/// </summary>
/// <param name="syn">our ISynchronizeInvoke object</param>
/// <param name="notify">our ProcessStatus object</param>
public CrossThreadCommunication(ISynchronizeInvoke syn, ProcessStatus notify)
{
	//set the _synch & _status 
	_synch = syn;
	_status = notify;
}

/// <summary>
/// empty constructor
/// </summary>
public CrossThreadCommunication()
{
	_synch = null;
	_status = null;
}
#endregion



Next, we get to the heart of our class, the methods that do our work. The first method will initialize our Thread. In this method we use the IsBackground Property of the thread to tell it to run in the background, then we name the thread and start it:

#region StartProcess
public void StartProcess()
{
	//we need to create a new thread for our process
	_thread = new System.Threading.Thread(AddItemsToList);
	//set the thread to run in the background
	_thread.IsBackground = true;
	//name our thread (optional)
	_thread.Name = "Add List Items Thread";
	//start our thread
	_thread.Start();
}
#endregion



When you create a new Thread you need to pass it the name of the process that the thread will be executing. In this case we are passing it the AddItemsToList, which we use to add items to our Collection<string>. While we are adding to our collection, we also lock the collection so it cannot be accessed by any other threads, preventing a race condition:

#region AddItemsToList
/// <summary>
/// method for adding items to our list
/// </summary>
private static void AddItemsToList()
{
	//create a loop to add items to the List<>
	//for demonstration purposes
	for (int i = 0; i <= 100; i++)
	{
		//lock the List<> so no other threads
		//can access it while our thread is working
		lock (_listItems)
			//add items to the list
			_listItems.Add("List Item #: " + _listItems.Count);
		//send message to update the UI
		UpdateStatus("Adding...", i);
		//sleep for 1/2 a second to look like its actually doing something 
		Thread.Sleep(500);
	}
}
#endregion



Since this is merely an example we aren't really doing anything big here, we are simply looping from 0 to 100 adding the counter object's value to our list. We then, on each iteration through the loop, use Sleep Method of the Thread Class to make it appear to be a larger process than it really is. You will notice that right before the Sleep method there is a call to a method named UpdateStatus. UpdateStatus is the method we are using to send our message to the UI Thread.

Here we create an object array that holds 2 items. You will also notice the method requires 2 parameters, msg and status, these parameters will be used to populate our object array. We then use the Invoke Method of the ISynchronizeInvoke Interface:

#region UpdateStatus
private static void UpdateStatus(string msg, string status)
{
	//create our Object Array
	object[] items = new object[2];
	//populate our array with the parameters
	//passed to our method
	items[0] = msg;
	items[1] = status;
	//call the delegate
	_synch.Invoke(_status, items);
}
#endregion



Using this we can update any UI component in the main thread without worrying if the call is being made from the proper thread. So as you can see, there is a thread-safe way to share data between threads. Though we aren't using data, we're simply updating a component on the UI with the status of our background thread. This process can now rune while leaving the UI in a usable state.

That is the end of the tutorial on Cross Thread Communication in C#, I hope you found this tutorial useful and informative. As I stated before, this is just the first in a series on multi-threading in C#, and what is available in the System.Threading Namespace.

Happy Coding :)

Is This A Good Question/Topic? 8
  • +

Replies To: Cross Thread Communication in C#

#2 RaymondKwan  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 01-December 08

Posted 01 December 2008 - 05:54 AM

In the example, ISchronizeInvoke is an Interface, do we not need an implementation to get it work? If so, could you please give the code? Thanks.
Was This Post Helpful? 0
  • +
  • -

#3 Marko Matic  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 1
  • Joined: 09-March 09

Posted 09 March 2009 - 11:08 AM

View PostRaymondKwan, on 1 Dec, 2008 - 04:54 AM, said:

In the example, ISchronizeInvoke is an Interface, do we not need an implementation to get it work? If so, could you please give the code? Thanks.


ISchronizeInvoke is Interface that for example Form has implemented... so you can just pass Form object as an ISchronizeInvoke argument... :ph34r:

So for example in one of my constructors...
public Risiver(StringBuilder bafer, Form WINDOW, ProcessStatus statusDelegat)



I have WINDOW argument, that is Form object, that I use as ISchronizeInvoke like this:

//Class field
private ISynchronizeInvoke sin;

***

//Inside constructor shown above
this.sin = (ISynchronizeInvoke) WINDOW;


Was This Post Helpful? 1
  • +
  • -

#4 Guest_User*


Reputation:

Posted 16 July 2010 - 05:31 AM

I would be really nice if you gave a complete example. How can the reader tell what code goes in Thread class and what code goes in the form class?
Was This Post Helpful? 0

#5 Guest_csharper*


Reputation:

Posted 17 August 2010 - 02:17 PM

Agree with the previous comment - a complete example would really help. If I knew what code went where, I wouldn't need the article!
Was This Post Helpful? 0

#6 Guest_frankg*


Reputation:

Posted 23 August 2010 - 07:09 AM

I couldn't believe it. After going through all kinds of gyrations for more than a day, I finally found the right article. This simple code did the trick for me:

public delegate void serviceGUIDelegate();
private void sendToGUI()
{
this.Invoke(new serviceGUIDelegate(serviceGUI));
}

"serviceGUI()" is a GUI level method within the form (this) that can change as many controls as you want. Just call sendToGUI() from the other thread. Parameters can be added to pass values, or just use class scope variables.
Was This Post Helpful? 1

#7 Guest_darkroastjava*


Reputation:

Posted 05 October 2010 - 02:02 AM

View Postcsharper, on 17 August 2010 - 01:17 PM, said:

Agree with the previous comment - a complete example would really help. If I knew what code went where, I wouldn't need the article!


Don't agree: If you don't understand where this code belongs, you don't understand the code. If so, you first need to understand the basic concepts of threading and should read another resource.

The example is (almost) complete. All the code listed goes in one class. The only thing missing is an example how to use that class, in particular what can be passed as a ISynchronizeInvoke. An example would be to use the form:

class MyForm : Form
{
    ...
    private StartButton_Click(object sender, EventArgs e)
    {
        MyCoolThreadingTool tool = new MyCoolThreadingTool(
            this,
            delegate(string message, int status)
            {
                this.ProgressLabel.Text = message;
            });

        tool.StartProcess();
    }
}

Was This Post Helpful? 1

#8 Tangela  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 47
  • Joined: 08-October 10

Posted 11 October 2010 - 12:56 PM

great work thx man
Was This Post Helpful? 0
  • +
  • -

#9 stag92  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 15-May 12

Posted 15 May 2012 - 07:25 AM

View Postfrankg, on 23 August 2010 - 07:09 AM, said:

I couldn't believe it. After going through all kinds of gyrations for more than a day, I finally found the right article. This simple code did the trick for me:

public delegate void serviceGUIDelegate();
private void sendToGUI()
{
this.Invoke(new serviceGUIDelegate(serviceGUI));
}

"serviceGUI()" is a GUI level method within the form (this) that can change as many controls as you want. Just call sendToGUI() from the other thread. Parameters can be added to pass values, or just use class scope variables.

This is perfect, that's what i've been looking for last few days. Everybody writes about delegates, an.pipes or named pipes. Here is a simple answer for my big problem. Thanks for telling me how invoke sth i the parent's context. Great!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1