Friday, April 3, 2009

What to do if your C# app shows messages "not responding" at the top of the window.

Sometimes you might see messages "not responding" at the top of the window when you are developing a C# Windows form application.

It if comes back soon,you can leave it as it is.But in case it doesn't,you must do something.So in this post,I will explain why this phenomenon happens and how to prevent it.

The cause of the phenomenon "not responding"
To avoid the phenomenon,the first thing you should do is to know how a Windows application respond to user operations.

When something happens on computer,for example a Button on your application is clicked,Windows Operation System send a information about the incident to the related application.The information is called "message" and the something having happened called "Event".

Every application on Windows function by handling those messages.

To tell this mechanism in more detail,Each application has its own "message queue" and Windows feed a message into the queue every time an event occurs.Every application must have a thread which checks if messages have been sent in the message queue at regular intervals and if they have,handle those messages one by one. Processed Messages are removed from message queue.This whole process that a thread is doing is called "Message loop".

In C# Windows Application,Message loop starts when Application.Run() is called,which is usually written in the auto generated code "Program.cs".

As long as "Message loop" keeps working within a given length of time,application continue responding without showing messages "not responding".

But if a time-consuming message comes into message queue,for example it requires to execute complicate queries,in the meantime the other messages stuck in the message queue.When your code go into this situation,your application will show messages "not responding".Sometimes it happens that "not responding" is followed by the window white out.This is because a message telling your application to draw itself is kept waiting in the queue.

What to do prevent the phenomenon
Now that we know the mechanism,we are very close to our goal.Actually we have several ways to solve the problem.

The easiest way is to call Application.DoEvents() somewhere in the handler code,in case that it has already taken a long time by then.By calling the method the execution goes into handling the other messages first.Then when it finishes,it will return to the beginning of the next line.


private void _searchBtn_Click(object sender, EventArgs e)
{
ExecuteQuery(); //Suppose this takes lots of time
Application.DoEvents();
ExecuteUpdate();
}
But if there is another time-consuming one in the other messages or a single atomic line takes a long time,this manner doesn't work.

Given this factor,We move into thread.Instead of having message loop thread do everything,it can start another thread which actually do the job.This let the main thread return quickly and take care of the others.

To do this,We have three options.the code below is directly starts a thread.



private void _searchBtn_Click(object sender, EventArgs e)
{
try
{
Thread thread = new Thread(new ThreadStart(Execute));
thread.Start();
}
catch {}
}

private void Execute()
{
//Write here what you want to do asyncrously.
}
If you need a foreground thread,this is the only your option,but be aware of overhead of thread creation and disposing.

The code below is the second option.ThreadPool frees your code from the overhead.
Thread pool threads are background threads.If you need a foreground thread,you must go with the first one.


private void _searchBtn_Click(object sender, EventArgs e)
{
try
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Execute));
}
catch {}
}

private void Execute()
{
//Write here what you want to do asyncrously.
}

QueueUserWorkItem() queue a method for execution,in most of the case execution of the method will begin in a moment.

The third option is callBack delegate.Only diffrence from the second one is forms of codes.
CallBack delegate uses ThredPool internally.



private delegate void ExecuteSearchDelegate();

private void _searchBtn_Click(object sender, EventArgs e)
{
try
{
ExecuteSearchDelegate dlgt = new ExecuteSearchDelegate(Execute);
dlgt.BeginInvoke(new AsyncCallback(ExecuteCallback), dlgt);
}
catch {}
}

private void Execute()
{
//Write here what you want to do asyncrously.
}

private void ExecuteCallback(IAsyncResult ar)
{
//Write here what you want to do after the delegate has completed.
}
Be careful,you can't access the controls in the code block executed by a thread that is not message loop.
In the case,you should do like below.



private void _searchBtn_Click(object sender, EventArgs e)
{
try
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Execute));
}
catch {}
}

private void Execute()
{
//Write here what you want to do asyncrously.

Invoke((MethodInvoker)delegate()
{
//Accessing controls.
});
}