views:

156

answers:

3

Hi,

I am trying to run a sample code (a very basic one) involving threading and critical regions.

Here's my code:

public static void DoCriticalWork(object o)
        {
            SomeClass instance = o as SomeClass;

            Thread.BeginCriticalRegion();
            instance.IsValid = true;
            Thread.Sleep(2);
            instance.IsComplete = true;
            Thread.EndCriticalRegion();

            instance.Print();
        }

And I am calling it as follows:

private static void CriticalHandled()
        {
            SomeClass instance = new SomeClass();
            ParameterizedThreadStart operation = new ParameterizedThreadStart(CriticalRegion.DoCriticalWork);
            Thread t = new Thread(operation);
            Console.WriteLine("Start thread");
            t.Start(instance);
            Thread.Sleep(1);
            Console.WriteLine("Abort thread");
            t.Abort();

            Console.WriteLine("In main");
            instance.Print();
        }

However, the output I get is:

**

Start thread
Abort thread
In main
IsValid: True
IsComplete: False

**

Since the critical region is defined, IsComplete should be true and not false.

Can someone please explain why it is not working?

Here is SomeClass for reference:

public class SomeClass
    {
        private bool _isValid;

        public bool IsValid
        {
            get { return _isValid; }
            set { _isValid = value; }
        }

        private bool _isComplete;

        public bool IsComplete
        {
            get { return _isComplete; }
            set { _isComplete = value; }
        }

        public void Print()
        {
            Console.WriteLine("IsValid: {0}", IsValid);
            Console.WriteLine("IsComplete: {0}", IsComplete);
            Console.WriteLine();
        }
    }

Edit

Expln from MCTS notes: The idea behind a critical region is to provide a region of code that must be executed as if it were a single line. Any attempt to abort a thread while it is within a critical region will have to wait until after the critical region is complete. At that point, the thread will be aborted, throwing the ThreadAbortException. The difference between a thread with and without a critical region is illustrated in the following figure:

Critical Region

+4  A: 

Thread.BeginCriticalRegion does not prevent a Thread from being aborted. I believe it is used to notify the runtime that if the Thread is aborted in the critical section, it is not necessarily safe to continue running the application/AppDomain.

The MSDN docs have a more complete explanation: http://msdn.microsoft.com/en-us/library/system.threading.thread.begincriticalregion.aspx

Andy White
But Andy, the MCTS notes that I am referring say that all the lines within the critical region should be treated as a single LOC and the abort should wait until execution of the critical section is over. I have edited the Q to include relevant section from the MCTS notes.
Rashmi Pandit
A: 

Its a sleep timing problem. Just widen the gap between the two sleeps and you will get the answer.

There are two threads: The main thread and the thread that does the critical work. Now when abort is called the thread 't' will abort instantly even if it has not completed the critical region.

Now as you have send the main thread to sleep for 2ms and thread t for 1ms, sometimes t will complete the critical section and sometimes it will not. So that's why the value for IsComplete is sometimes false and sometimes true.

Now just send the main thread to sleep for 100ms and you will find the IsComplete is always true. Vice versa send the thread "t" to sleep for 100ms and you will find the IsComplete is always false.

EDIT

FROM MSDN

Notifies a host that execution is about to enter a region of code in which the effects of a thread abort or unhandled exception might jeopardize other tasks in the application domain.

For example, consider a task that attempts to allocate memory while holding a lock. If the memory allocation fails, aborting the current task is not sufficient to ensure stability of the AppDomain, because there can be other tasks in the domain waiting for the same lock. If the current task is terminated, other tasks could be deadlocked.

When a failure occurs in a critical region, the host might decide to unload the entire AppDomain rather than take the risk of continuing execution in a potentially unstable state. To inform the host that your code is entering a critical region, call BeginCriticalRegion. Call EndCriticalRegion when execution returns to a non-critical region of code.


From CLR Inside Out: Writing Reliable Code

State Corruption There are three buckets that state corruption may fall into. The first is local state, which includes local variables and heap objects that are only used by a particular thread. The second is shared state, which includes anything shared across threads within the AppDomain, such as objects stored in static variables. Caches often fall into this category. The third is process-wide, machine-wide, and cross-machine shared state—files, sockets, shared memory, and distributed lock managers fall into this camp.

The amount of state that can be corrupted by an async exception is the maximum amount of state a thread is currently modifying. If a thread allocates a few temporary objects and doesn't expose them to other threads, only those temporary objects can be corrupted. But if a thread is writing to shared state, that shared resource may be corrupted, and other threads may potentially encounter this corrupted state. You must not let that happen. In this case, you abort all the other threads in the AppDomain and then unload the AppDomain. In this way, an asynchronous exception escalates to an AppDomain, causing it to unload and ensuring that any potentially corrupted state is thrown away. Given a transacted store like a database, this AppDomain recycling provides resiliency to corruption of local and shared state.

Critical Regions allow you to handle situations where some piece of code might corrupt other Application Domains and causing irreparable damage to the system.

Aseem Gautam
A: 

A good solution is to encapsulate the code from DoCriticalWork() with


try { ... } catch(ThreadAbortedException) {...}


where you should act as you wish (maybe set IsComplete = true?)

You can read more about ThreadAbortException, and be sure to check Thread.ResetAbort and the finally block usage for this case.

As Andy mentioned and quoting from Thread.BeginCriticalRegion:

Notifies a host that execution is about to enter a region of code in which the effects of a thread abort or unhandled exception might jeopardize other tasks in the application domain.

foliveira