C# Basic (9)

1. Context Switching

The processor uses a hardware timer to determine when a timeslice has ended for a given thread. When the hardware timer signals the interrupt, the processor saves all registers for the current thread onto the stack. Then the processor moves those same registers from the stack into a data structure called a CONTEXT structure. When the processor wants to switch back to a previously executing thread, it reverses this procedure and restores the registers from the CONTEXT structure associated with the thread. This entire procedure is called context switching.

 

2. ThreadStart WorkerThreadMethod = new ThreadStart(WorkerThreadMethod);

Any time you see the following form, you can be sure that x is a delegate or “Delegates and Event Handlers“—a definition of a method signature:

x varName = new x (methodName);

 

3. AppDomain: a logical process inside of a physical process.

In Win32, a thread is confined to a single process, as you saw when I described context switching earlier. A thread in one process cannot invoke a method in a thread that belongs to another process. In .NET, however, threads can cross AppDomain boundaries, and a method in one thread can call a method in another AppDomain.

 

4. The Thread.Suspend method can be called on the currently executing thread or another thread. Once a thread is suspended in this fashion, only another thread can cause its resumption, with the Thread.Resume method.

Once a thread suspends another thread, the first thread is not blocked. The call returns immediately. Also, regardless of how many times the Thread.Suspend method is called for a given thread, a single call to Thread.Resume will cause the thread to resume execution.

 

5. If the need should arise to destroy a thread, you can accomplish this with a call to the Thread.Abort method. The runtime forces the abortion of a thread by throwing a ThreadAbortException. Even if the method attempts to catch the ThreadAbortException, the runtime won’t let it. However, the runtime will execute the code in the aborted thread’s finally block, if one’s present.

when the Thread.Abort method is called, the thread will not cease execution immediately. The runtime waits until the thread has reached what the documentation describes as a “safe point.” Therefore, if your code is dependent on something happening after the abort and you must be sure the thread has stopped, you can use the Thread.Join method. This is a synchronous call, meaning that it will not return until the thread has been stopped.

 

6. Each thread has an associated priority level that tells the processor how it should be scheduled in relation to the other threads in the system. This priority level is defaulted to Normal—more on this shortly—for threads that are created within the runtime. For threads that are created outside the runtime, they retain their original priority. You use the Thread.Priority property to view and set this value. The Thread.Priority property’s setter takes a value of type Thread.ThreadPriority that is an enum that defines these values: Highest, AboveNormal, Normal, BelowNormal, and Lowest.

t1.Priority = ThreadPriority.Highest;

t2.Priority = ThreadPriority.Lowest;

The thread having the highest value is the thread with the highest priority. When you have a situation where several threads are running at the same priority, they’ll all get an equal amount of processor time. This is called round robin scheduling.

 

7.

using System;
using System.Threading;

class Database
{
    public void SaveData(string text)
    {
        Monitor.Enter(this);

        Console.WriteLine("Database.SaveData - Started");

        Console.WriteLine("Database.SaveData - Working");
        for (int i = 0; i < 100; i++)
        {
            Console.Write(text);
        }

        Console.WriteLine("\nDatabase.SaveData - Ended");

        Monitor.Exit(this);
    }
}

class ThreadMonitor2App
{
    public static Database db = new Database();

    public static void WorkerThreadMethod1()
    {
        Console.WriteLine("Worker thread #1 - Started");

        Console.WriteLine
           ("Worker thread #1 - Calling Database.SaveData");
        db.SaveData("x");

        Console.WriteLine("Worker thread #1 - Returned from Output");
    }

    public static void WorkerThreadMethod2()
    {
        Console.WriteLine("Worker thread #2 - Started");

        Console.WriteLine
            ("Worker thread #2 - Calling Database.SaveData");
        db.SaveData("o");

        Console.WriteLine("Worker thread #2 - Returned from Output");
    }

    public static void Main()
    {    
        ThreadStart worker1 = new ThreadStart(WorkerThreadMethod1);
        ThreadStart worker2 = new ThreadStart(WorkerThreadMethod2);

        Console.WriteLine("Main - Creating worker threads");

        Thread t1 = new Thread(worker1);
        Thread t2 = new Thread(worker2);

        t1.Start();
        t2.Start();
    }
}

Notice in the following output that even though the second thread called the Database.SaveData method, the Monitor.Enter method caused it to block until the first thread had released its lock:

 

Main - Creating worker threads

Worker thread #1 - Started
Worker thread #2 - Started

Worker thread #1 - Calling Database.SaveData
Worker thread #2 - Calling Database.SaveData

Database.SaveData - Started
Database.SaveData - Working
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Database.SaveData - Ended

Database.SaveData - Started

Worker thread #1 - Returned from Output

Database.SaveData - Working
ooooooooooooooooooooooooooooooooooooooo
Database.SaveData - Ended

Worker thread #2 - Returned from Output

8. Using lock

class Database
{
    public void SaveData(string text)
    {
        lock(this)
                {
            Console.WriteLine("Database.SaveData - Started");

            Console.WriteLine("Database.SaveData - Working");
            for (int i = 0; i < 100; i++)
            {
                Console.Write(text);
            }

            Console.WriteLine("\nDatabase.SaveData - Ended");
        }
    }
}

 

9. Mutex

You can create a mutex in C# with the following three constructors:

Mutex( )
Mutex(bool initiallyOwned)
Mutex(bool initiallyOwned, string mutexName)

The first constructor creates a mutex with no name and makes the current thread the owner of that mutex. Therefore, the mutex is locked by the current thread. The second constructor takes only a Boolean flag that designates whether the thread creating the mutex wants to own it (lock it). And the third constructor allows you to specify whether the current thread owns the mutex and to specify the name of the mutex.

The WaitOne method is also overloaded to provide more flexibility in terms of allowing you to define how much time the thread will wait for the mutex to become available. Here are those overloads:

WaitOne( )
WaitOne(TimeSpan time, bool exitContext)
WaitOne(int milliseconds, bool exitContext)

The basic difference between these overloads is that the first version—used in the example—will wait indefinitely, and the second and third versions will wait for the specified amount of time, expressed with either a TimeSpan value or an int value.

class Database
{
    Mutex mutex = new Mutex(false);

    public void SaveData(string text)
    {
        mutex.WaitOne();

        Console.WriteLine("Database.SaveData - Started");

        Console.WriteLine("Database.SaveData - Working");
        for (int i = 0; i < 100; i++)
        {
            Console.Write(text);
        }

        Console.WriteLine("\nDatabase.SaveData - Ended");

        mutex.Close();
    }
}

 

 

 

Advertisements
This entry was posted in C#. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s