线程的基础概念

关于时间片

线程的基础概念_第1张图片 

Here is the explaination 

Time Slices

With all these processes all wanting a slice of the CPU time cycle, how does it get managed? Well, each process is granted a slice of time (quantum) on which it (the process) may use the CPU. This slice of time should never be considered a constant; it is affected by the OS and the CPU type. 

每个操作系统会定义不同的时间片大小,时间片指处理器给予进程的处理时间,进程能执行的时间称为时间量或时间片。这个模型表明,线程是按时间片依次进行,下一个时间片的

线程可能属于同一进程,也可以属于另外一个进程。

Note: Only one thread actually runs on the CPU at one time.

If we go back to the Task Manager and change the view to include the thread count, we can see something like: 

线程的基础概念_第2张图片 

This shows that each process can clearly have more than one  thread . So how's all this scheduling and state information managed? We will consider that next. 

Thread Local Storage

When a threads time slice has expired, it doesn't just stop and wait its turn. Recall that a CPU can only run onethread at a time, so the current thread needs to be replaced with the next thread to get some CPU time. Before that happens, the current thread needs to store its state information to allow it to execute properly again. This is what the TLS is all about. One of the registers stored in the TLS is the program counter, which tells the thread which instruction to execute next.

Interrupts

Processes don't need to know about each other to be scheduled correctly. That's really the job of the Operating System. Even OSs have a main thread, sometimes called the system thread, which schedules all other threads. It does this by using interrupts. An interrupt is a mechanism that causes the normal execution flow to branch somewhere else in the computer memory without the knowledge of the execution program.

The OS determines how much time the thread has to execute, and places an instruction in the current thread's execution sequence. Since the interrupt is within the instruction set, it's a software interrupt, which isn't the same as a hardware interrupt.

Interrupts are a feature used in all but the simplest microprocessors, to allow hardware devices to request attention. When an interrupt is received, a microprocessor will temporarily suspend execution of the code it was running and jump to a special program called an interrupt handler. The interrupt handler will typically service the device needing attention, and then returns to the previously-executing code.

One of the interrupts in all modern computers is controlled by a timer, whose function is to demand attention at periodic intervals. The handler will typically bump some counters, see if anything interesting is supposed to happen, and if there's nothing interesting (yet), return. Under Windows, one of the 'interesting' things that can happen is the expiry of a thread's time slice. When that occurs, Windows will force execution to resume in a different thread from the one that was interrupted.

Once an interrupt is placed, the OS then allows the thread to execute. When the thread comes to the interrupt, the OS uses a special function called an interrupt handler to store the thread's state in the TLS. Once the thread time slice has timed out, it is moved to the end of the thread queue for its given priority (more on this later) to wait its turn again. 

线程的基础概念_第3张图片 

This is OK if the thread isn't done or needs to continue executing. What happens if the thread decides it does not need any more CPU time just yet (maybe wait for a resource), so yields its time slice to another thread?

This is down to the programmer and the OS. The programmer does the yield (normally using the Sleep() method); the thread then clears any interrupts that the OS may have placed in its stack. A software interrupt is then simulated. The thread is stored in the TLS and moved to the end of the queue as before.

The OS may have, however, already placed an interrupt in the threads stack, which must be cleared before thethread is packed away; otherwise, when it executes again, it may get interrupted before it should be. The OS does this (thank goodness). 

 

关于中断和线程本地存储器的工作原理

当线程在规定的时间片里完成不了,那么为了在接下来的某个时间片里继续执行,线程需记录当前的状态,以便将来能继续运行下。

这些状态信息是保存在线程本地存储器(即TLS)中的。根据以上可以得知,里面至少包含的信息是:记录了下一次需运行的指令地址。

当线程的时间片执行结束后,系统将会执行中断指令,中断指令将上执行上的行为,也就是将状态信息保存在TLS队列中。TLS将会保存在执行队列的末尾。

线程的基础概念_第4张图片 

Thread Sleep and Clock Interrupts

As we just said, a thread may decide to yield its CPU time to wait for a resource, but this could be 10 or 20 minutes, so the programmer may choose to make the thread sleep, which results in the thread being packed in the TLS. But it doesn't go to the runnable queue; it goes to a sleep queue. In order for threads in the sleep queue to run again, they need a different kind of interrupt, called a clock interrupt. When a thread enters the sleep queue, a new clock interrupt is scheduled for the time that the thread should be awoken. When a clock interrupt occurs that matches an entry on the sleep queue, the thread is moved back to the runnable queue. 

线程的基础概念_第5张图片 

 

关于线程睡眠和时钟中断

线程也可以将自己的执行时间片让给其它线程,线程希望能退出执行队列比较长的时间,这段时间我们可以称之为睡眠时间,同理,与此同时,线程的执行信息也被保存到TLS中,

不过,和之前的中断不同的是,不会将TLS并没有放到运行队列的末尾,而是放在一个独立的睡眠队列中,为了使睡眠队列中的线程再次恢复运行,也需要添加一个中断,这种中断

称为时钟中断,当时钟中断出现时,就会判断时间是否睡眠队列中某个线程的恢复运行时间匹配时,那该线程将恢复运行。 

线程的基础概念_第6张图片 

要使线程进入睡眠中,只需要在线程中调用Thread.sleep方法即可。 

关于进程,应用程序域、线程之间的关系图

线程的基础概念_第7张图片 

 

Thread Abort / Thread Done

All things have an end. When a thread is finished or it is programmatically aborted, the TLS for that thread is de-allocated. The data in the process remains (remember, it's shared between all the process threads, there could be more than one), and will only be de-allocated when the process itself is stopped.

So, we've talked a bit about scheduling, but we also said the TLS stored state for threads, how does it do this? Well, consider the following from MSDN:

"Threads use a local store memory mechanism to store thread-specific data. The Common Language Runtime allocates a multi-slot data store array to each process when it is created. The thread can allocate a data slot in the data store, store and retrieve a data value in the slot, and free the slot for reuse after the thread expires. Data slots are unique per thread. No other thread (not even a child thread) can get that data.

If the named slot does not exist, a new slot is allocated. Named data slots are public, and can be manipulated by anyone."

That's how in a nutshell. Let's see the MSDN example (blatant steal here): 

using System;

using System.Threading;

namespace TLSDataSlot
{
    class Program
    {
        static void Main()
        {
            Thread[] newThreads = new Thread[4];
            for (int i = 0; i < newThreads.Length; i++)
            {
                newThreads[i] =
                    new Thread(new ThreadStart(Slot.SlotTest));
                newThreads[i].Start();
            }
        }
    }

    class Slot
    {
        static Random randomGenerator = new Random();

        public static void SlotTest()
        {
            // Set different data in each thread's data slot.
            Thread.SetData(
                Thread.GetNamedDataSlot("Random"),
                randomGenerator.Next(1, 200));

            // Write the data from each thread's data slot.
            Console.WriteLine("Data in thread_{0}'s data slot: {1,3}",
                AppDomain.GetCurrentThreadId().ToString(),
                Thread.GetData(
                Thread.GetNamedDataSlot("Random")).ToString());

            // Allow other threads time to execute SetData to show
            // that a thread's data slot is unique to the thread.
            Thread.Sleep(1000);

            Console.WriteLine("Data in thread_{0}'s data slot is still: {1,3}",
                AppDomain.GetCurrentThreadId().ToString(),
                Thread.GetData(
                Thread.GetNamedDataSlot("Random")).ToString());

            // Allow time for other threads to show their data,
            // then demonstrate that any code a thread executes
            // has access to the thread's named data slot.
            Thread.Sleep(1000);

            Other o = new Other();
            o.ShowSlotData();
            Console.ReadLine();
        }
    }

    public class Other
    {
        public void ShowSlotData()
        {
            // This method has no access to the data in the Slot
            // class, but when executed by a thread it can obtain
            // the thread's data from a named slot.
            Console.WriteLine(
                "Other code displays data in thread_{0}'s data slot: {1,3}",
                AppDomain.GetCurrentThreadId().ToString(),
                Thread.GetData(
                Thread.GetNamedDataSlot("Random")).ToString());
        }
    }
}

This may produce the following:

线程的基础概念_第8张图片 

It can be seen that this uses two things:

  • GetNamedDataSlot: looks up a named slot
  • SetData: sets the data in the specified slot within the current thread

There is another way; we can also use the ThreadStaticAttribute which means the value is unique for eachthread. Let's see the MSDN example (blatant steal here): 

using System;
using System.Threading;

namespace ThreadStatic
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 3; i++)
            {
                Thread newThread = new Thread(ThreadData.ThreadStaticDemo);
                newThread.Start();
            }
        }
    }

    class ThreadData
    {
        [ThreadStaticAttribute]
        static int threadSpecificData;

        public static void ThreadStaticDemo()
        {
            // Store the managed thread id for each thread in the static
            // variable.
            threadSpecificData = Thread.CurrentThread.ManagedThreadId;

            // Allow other threads time to execute the same code, to show
            // that the static data is unique to each thread.
            Thread.Sleep(1000);

            // Display the static data.
            Console.WriteLine("Data for managed thread {0}: {1}",
                Thread.CurrentThread.ManagedThreadId, threadSpecificData);
        }
    }

}

And this may produce the following output: 

 

上面表现的是如何为每个线程保存各自的数据,且只能被各自的线程所访问。两种方式。 

See Also

http://www.codeproject.com/Articles/26148/Beginners-Guide-to-Threading-in-NET-Part-1-of-n 

 

你可能感兴趣的:(线程)