A traditional process has a single thread of control and a single program counter.
Threads are a way for a program to split itself into two or more simultaneously running tasks.Modern operating systems provide multiple threads of control within a process - these are called Threads or lightweight processes. All threads within a process share the same address space. This means that a thread shares its code and data section with other threads. Each, however, has its own program counter, stack and register set. The program counter etermines which instruction the thread is currently executing. This may or may not be the same as that for other threads. The register set is needed because when threads are suspended, their state must be saved. Upon resumption, this state is loaded into the machine registers.
Like traditional processes, threads can be in different states - running, ready or blocked .
Why do we have them? To see why multiple threads of control within a process are useful, consider what a web browser such as Firefox must do to load a web page containing multiple images. For each image, it must set up a separate connection to the page's home site and request the image.
[This multithreading generally occurs by time slicing (where a single processor switches between different threads) or by multiprocessing (where threads are executed on separate processors). Threads are similar to processes, but differ in the way that they share resources.] By having multiple threads within the browser process, the images can be requested in parallel with the user scrolling through the content that has already been received, greatly enhancing the response time for the user. Also, extensive sharing of resources makes CPU switching between threads inexpensive, compared with switching between traditional or heavyweight processes. A register-set switch is still required but no memory-management related work needs to be done.
How do Threads Work? Two kinds of implementation schemes are in vogue for threads - user-level and kernel-level. User-level threads are managed entirely in user space. The operating system is not aware of their existence. Thread switching does not involve a call to the operating system or an interrupt to the kernel. Switching is thus independent of the operating system and very quick. One disadvantage of this scheme is that if a user-level thread blocks while executing a system call, the kernel blocks the entire process since it is not even aware that other threads exist.
In systems supporting kernel-level threads, the operating system is aware of the existence of multiple threads per process, so when a thread blocks, the operating system chooses the next one to run, either from the same process or a different one.
Some systems support a hybrid approach in which both user-level and kernel-level threads are supported. Solaris 2 is one such system.
Synchronizing Threads With Mutexes
One of the basic problems when running several threads that use the same memory space, is making sure they don't "step on each other's toes". By this we refer to the problem of using a data structure from two different threads.
For instance, consider the case where two threads try to update two variables. One tries to set both to 0, and the other tries to set both to 1. If both threads would try to do that at the same time, we might get with a situation where one variable contains 1, and one contains 0. This is because a context-switch (See below if you don't know what is it) might occur after the first tread zeroed out the first variable, then the second thread would set both variables to 1, and when the first thread resumes operation, it will zero out the second variable, thus getting the first variable set to '1', and the second set to '0'.For example, consider the classical producer-consumer problem:
Assume counter is initially 5. One interleaving of
statements is:
producer: register1 = counter (register1 = 5)
producer: register1 = register1 + 1 (register1 = 6)
consumer: register2 = counter (register2 = 5)
consumer: register2 = register2 – 1 (register2 = 4)
producer: counter = register1 (counter = 6)
consumer: counter = register2 (counter = 4)
Thread class in C++:
class Thread
{
public:
Thread();
int Start(void * arg);
protected:
int Run(void * arg);
static void * EntryPoint(void*);
virtual void Setup();
virtual void Execute(void*);
void * Arg() const {return Arg_;}
void Arg(void* a){Arg_ = a;}
private:
THREADID ThreadId_;
void * Arg_;
};
Thread::Thread() {}
int Thread::Start(void * arg)
{
Arg(arg); // store user data
int code = thread_create(Thread::EntryPoint, this, & ThreadId_);
return code;
}
int Thread::Run(void * arg)
{
Setup();
Execute( arg );
}
/*static */
void * Thread::EntryPoint(void * pthis)
{
Thread * pt = (Thread*)pthis;
pthis->Run( Arg() );
}
virtual void Thread::Setup()
{
// Do any setup here
}
virtual void Thread::Execute(void* arg)
{
// Your code goes here
}Some useful resources:users.actcom.co.il/~choo/lupg/tutorials/multi-thread
/multi-thread.html
en.wikipedia.org/wiki/Thread_%28computer_science%29www.personal.kent.edu/~rmuhamma/OpSystems/Myos
/threads.htmftp://download.intel.com/support/performancetools/threadchecker
/threads.htm
msdn.microsoft.com/library/default.asp?url=/library/en-us
/cpref/html/frlrfsystemthreadingthreadclasstopic.aspwww.mcs.vuw.ac.nz/courses/COMP305/2004T1/Nachos
/Salsa/NachosFiles/threadsContext Switch: When a multitasking-operating system stops running one process and starts running another. Many operating systems implement concurrency by maintaining separate environments or "contexts" for each process. The amount of separation between processes, and the amount of information in a context, depends on the operating system but generally the OS should prevent processes interfering with each other, e.g. by modifying each other's memory.
A context switch can be as simple as changing the value of the program counter and stack pointer or it might involve resetting the MMU to make a different set of memory pages available.
In order to present the user with an impression of parallism, and to allow processes to respond quickly to external events, many systems will context switch thousands of times per second.