Using scope in C#

October 8, 2010

Microsoft managed code uses the garbage collector to reclaim space when objects go out of scope. This is a great advantage in that it frees the programmer from worrying about releasing resources. The programmer can’t predict when garbage collection will occur though. Unfortunately that also prevents you from using scope to your advantage. You can’t just put code in the destructor and assume it will be called when the object goes out of scope. If you created a mutex object (like I did in a previous post about C++ scoping) and left the unlock in the destructor you’d have no way to guarantee when your mutex would get unlocked.

There’s a handy trick to get around that problem. You can use scoping to your advantage if you combine the “using” keyword and the IDisposable interface. The IDisposable interface adds a Dispose() method to your class. The using keyword guarantees to call the Dispose method of any objects that are created using the keyword. The combination of the two of them does what the C++ scoping does.

Here’s an example of a class that is useful for winforms programmers. If you’re doing a rigorous job you know you should change the cursor to provide a visual indicatation the computer is working on a task. It’s a pain to ensure that the cursor is always set back to it’s original state when you finish the task. If an exception occurs you can end up with the user thinking the machine is still working on the task when it’s not.

public class BusyCursor : IDisposable
{
private Cursor _PreviousCursor;
private Form _ParentForm;

/// <summary>
/// Constructor
/// Saves form’s current cursor
/// Changes the cursor to a busy cursor
/// </summary>
/// <param name="ParentForm"></param>
public BusyCursor( Form ParentForm )
{
_ParentForm = ParentForm;
_PreviousCursor = _ParentForm.Cursor;
_ParentForm.Cursor = Cursors.WaitCursor;
}

#region IDisposable Members

public void Dispose()
{
_ParentForm.Cursor = _PreviousCursor;
}

#endregion
}

This class saves the cursor currently used by the form you pass to it. It then changes the cursor to the busy cursor. When it goes out of scope it restores the cursor to the original state.

You use the class like this:

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void  button1_Click(object sender, EventArgs e)
{
using ( BusyCursor ShowBusyCursor = new BusyCursor(this) )
{
// long running process here
System.Threading.Thread.Sleep( 10000 );
}
}
}

It’s simple to implement, works very well, and isn’t terribly hard to understand.

Another day in the life of a workaday programmer batman.

++djs

Advertisements

Scoped locking and exceptions

September 11, 2010

If you’re using the scoped mutex from the previous post how do you guarantee that an exception won’t leave the mutex locked?

One of the features of C++ is when exceptions are thrown objects on the stack are destroyed. Since the destructor of the scoped mutex controller unlocks the mutex then we’re guaranteed that the mutex will not remain locked if an exception is thrown.

Just to be rigorous lets make sure this works. Here’s a very short program to lock and unlock the mutex normally, then try throwing an exception while it’s locked:

int main( int argc, char** argv )
{
 Mutex TestMutex;

 // normal operation
 {
 Access a(TestMutex);
 std::cout << "Test 1\n";
 }
 // with an exception
 try
 {
 Access a(TestMutex);
 std::cout << "Test 2 start\n";
 throw std::runtime_error("fail");
 std::cout << "Test 2 stop\n";
 }
 catch(...)
 {
 std::cout << "Test 2 catch\n";
 }

 return 0;
}

The output from the program is what is expected:

Output:
locked
Test 1
unlocked
locked
Test 2 start
unlocked
Test 2 catch

Note that the mutex is unlocked BEFORE your exception executes.

One other thing worth noting is if you throw and unhandled exception that terminates the program your lock will not be released. If you’re just locking threads within the program this isn’t a problem but if you’re using this technique for managing a persistent lock then you may have a problem. I don’t see this as a big drawback since unhandled exceptions are something you shouldn’t leave in your applications anyway!


Managing critical sections using scope in C++

September 4, 2010

Mutexs are a great way to manage multi-threaded applications. It makes sure that two threads can’t change something while it’s being used somewhere else.

The only drawback to using them is that you must unlock them when you’ve finished with the critical section of code. You can forget to unlock it, miss putting an unlock in a code path, or an exception can divert around your carefully crafted code.

A simple trick can make your life as a programmer easier. In C++ you can use scoping to define a critical section and it will manage your mutex for you.

Here’s a simple example class that mimics a Mutex:

class Mutex
{
public:
 Mutex() : state(false)
 {
 }
 void lock()
 {
 state = true;
 std::cout << "locked\n";
 }
 void unlock()
 {
 state = false;
 std::cout << "unlocked\n";
 }
 bool state;
};

I’ve added string output so you can see when the lock and unlock methods are called. The standard way to use it would be something like this:


TestMutex.lock();
 // do something
 TestMutex.unlock();

Let’s create a new class to manage the mutex for us using scoping:


class Access
{
public:
 Access( Mutex& singleton ) : m(singleton)
 {
 m.lock();
 }
 ~Access()
 {
 m.unlock();
 }
 Mutex& m;
};

The constructor of the Access class saves a reference to a mutex and locks the mutex. When the destructor is called the mutex is unlocked.

How do we use this?

  • Create a block that surrounds your critical section that you want to protect with a mutex
  • Instantiate an object of the Access class FIRST.

Here’s an example:


{
 Access a(TestMutex);
 std::cout << "Test 1\n";
 }

If you run this you’ll get this:


locked
 Test 1
 unlocked

It locks the mutex before the work is done and as soon as the program exits the block it calls the destructor of the Access and unlocks the mutex. It’s simple and makes it impossible to forget to unlock the mutex.

Hope you find this useful. In the next post I’ll show you how this protects you from failing to unlock your mutex if you get exceptions.