Monday, July 23, 2007

Cross thread operation failure in .NET 2.0

Cross thread operation failure in .NET 2.0 :

within the user created thread , if we tried to add or update the control, that will cause the Cross thread operation failure .

Description of the problem :

During Runtime, .NET CLR checks whether the control is accessed at the thread where it is created. otherwise .NET CLR will display the Cross thread operation failure.

For Example if we added the list box to the form, within the user created thread, if we add item to the list box , this will produce
cross thread operation failure error.

How to solve the cross thread operation failure problem :

All the controls are derived from Control class.

Thread Safety
Only the following members are safe for multithreaded operations: BeginInvoke, EndInvoke, Invoke, InvokeRequired, and CreateGraphics.

For other methods, the developer is responsible for thread safe.(The developer has to write code to make it as thread safe..)

How I solved the problem:
-------------------------------------

By calling the Invoke() method, I solved the problem...

Note : BeginInvoke() or Invoke() blocks the thread until it updates the data to the control.




delegate void UpdateControl(string text);

void UpdateList(string text)
{


if(listbox1.InvokeRequired)
{
UpdateControl updateCtl = new UpdateControl( UpdateList);
listbox1.Invoke(updateCtl, text);
}
else
{
listbox1.Items.Add(text); // Add item to the list box...
}
}


void ThreadProc () // Cross thread's operation failure
{
UpdateList( "Sundar");
}



InvokeRequired member is true, if the cross thread operation execution takes place. ( this will be done by the framework by comparing the thread instance)




Full sample application with code:



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace CrossThreadInvoke
{
public partial class Form1 : Form
{

private Thread thread1 = null;
private ThreadStart threadStart1 = null;
delegate void UpdateControl (string text);


public Form1()
{
InitializeComponent();
threadStart1 = new ThreadStart(ThreadProcedure);
thread1 = new Thread(threadStart1);


}
private void UpdateList(string text)
{
if (listBox1.InvokeRequired)
{
UpdateControl m_UpdateControl = new UpdateControl(UpdateList);
listBox1.Invoke(m_UpdateControl, text);
}
else
{
listBox1.Items.Add(text);
}
}

private void ThreadProcedure()
{
int i = 0;
while (true)
{
if (i > 10)
{
break;
}

UpdateList("Sundar");
i++;
}

thread1.Abort();
}
private void btnStart_Click(object sender, EventArgs e)
{
thread1.Start();
}

private void btnStop_Click(object sender, EventArgs e)
{
thread1.Abort();
}
}
}


BeginInvoke() :

it is working in the same way as Invoke() fn...

Check the following sample application :


BeginInvoke() Sample application :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;


namespace CrossThread
{
public partial class Form1 : Form
{
private Thread thread1 = null;
private ThreadStart threadStart1 = null;

public delegate void UpdateList(string text);
public UpdateList m_UpdateList = null;



public Form1()
{
InitializeComponent();
m_UpdateList += new UpdateList(SetToList1);
threadStart1 = new ThreadStart(AddToList1);
thread1 = new Thread(threadStart1);

}


public void SetToList1(string text)
{
listBox1.Items.Add(text);
}


private void AddToList1()
{
try
{
int i = 10;
while (true)
{
if (i <= 0) break;
listBox1.BeginInvoke(m_UpdateList, "sundar");
i--;
Thread.Sleep(1000);
}

}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());

}
}



Invoke() fns are thread safe.



In case of Invoke() fn what happens ...


void UpdateList(string text)
{
if( listbox1.InvokeRequired )
{
UpdateControl m_UpdateControl = new UpdateControl(UpdateList);
listbox1.Invoke( m_UpdateControl, text);
}
else
{
listbox1.Items.Add( text);
}
}


How this code works ...

Main thread is available to the C# application until we closes the application.
Application.Run() method in C# begins running a standard application message loop in the Form's main thread.
Each and Every thread has its own message queue for messages. if any event occurs in the thread will be added to the corresponding thread's message queue.


while we are accesing the listbox from cross thread, the thread instance is checked ...

For Cross thread, InvokeRequired field is set to true.


Invoke() fn executes a delegate on the thread that owns the control's underlying window handle.
The delegate is executed in the thread which the control is created . Until the completion of the delegate execution,
the current thread is blocked .

This is also same to BeginInvoke() fn . At which scenario we will use Invoke() and BeginInvoke()...

Normally for UI update we will use Invoke() method.
For UI data updation, we will use BeginInvoke() method.

For Example Updating the text to the list box we can use BeginInvoke() method.
For Example adding controls to the tab controls we may go for Invoke() fn. BeginInvoke() fn execution is much faster than Invoke() fn.

No comments: