Click to See Complete Forum and Search --> : A thread and a process
vikrampschauhan
January 12th, 2005, 02:56 PM
As I know of it, a process consists of one or more threads.
Threads in a process share the same memory area.
Critical Sections etc are required for instructions that are in the shared memory area.
___ start Critical Section _______
x=x+1;
print x;
___ end Critical Section ________
The code above is in the shared memory area. What is the shared memory area? I know it is the memory area being shared by more than one threads but, what else?
How do threads share a memory area(that is, how does context switching work)?
I know that each thread contains a stack of its own and a heap of its own. A process as such doesnot consist of a stack or a heap. These are attributed to threads. What all else does a thread consist of?
Thanks
Vikram
Andreas Masur
January 12th, 2005, 03:26 PM
Take a look at the following article (http://www.codeproject.com/threads/SharedMemory_IPC_Threads.asp)...
OReubens
January 12th, 2005, 03:46 PM
Threads in a process share the same memory area.
Critical Sections etc are required for instructions that are in the shared memory area.
You're off slightly here... :-)
Critical sections or other synchonisation objects are needed for those pieces of CODE where multiple threads can have simultaneous 'unsafe' access to global variables and/or resources (this includes class static variables!).
"Shared memory" is actually something quote different. It is a term for memory that is shared (identical) between two different processes.
You do not need critical sections (or other sync. objects) aroud everything that is global. Only those pieces of code where whatever two threads are doing could potentially cause undefined behaviour. If this sounds strange. It should, it's one of the most difficult things to grasp in multithreading... Multithreading requires a really different way about thinking of (even very basic) programming problems.
While you do NEED sync. objects, having too many of them will make your code run (significantly) slower, and that counteracts the whole reason about why you would want multiple threads.
If you have 2 threads, and all they ever do is READ a variable, there is no problem.
Even a write is not necessarily a problem, it depends on how you want the variable to behave if on thread writes to it and another reads, or even if both threads write.
The article Andreas referred to is interesting, but is somewhat beside the point in my opinion because it's about multiple processes, not multiple threads inside a single process. However, it does have merit, because some of the principles are the same.
Andreas Masur
January 12th, 2005, 04:21 PM
The article Andreas referred to is interesting, but is somewhat beside the point in my opinion because it's about multiple processes, not multiple threads inside a single process. However, it does have merit, because some of the principles are the same.
Well...indeed the article is not the best one suited here...I missed the one process part I guess....I read about shared memory...and usually this is used between processes rather than threads within the same process....well...at least...I haven't used it for that...
So...sorry about that...
vikrampschauhan
January 12th, 2005, 06:46 PM
Say I have the following code snippet:
int* pI=NULL;
main()
{
pI=new int(0);
CreateThread(Fun1);
CreateThread(Fun1);
}
Fun1()
{
*pI=*pI+1;
std:cout<<*pI;
}
comparing it to:
main()
{
int i=0;
int* pI=&i;
CreateThread(Fun1(pI));
CreateThread(Fun1(pI));
}
Fun1(int* pI)
{
*pI=*pI+1;
std:cout<<*pI;
}
The first code snippet has undefined behaviour. It may print 1 and then 2. Or it may print 2 twice.
The second code snippet also has undefined behavior. Though everything is on the stack and each thread has its own stack(and thus, less chance of manupulating the same data). I believe this is because the two threads(that were created using the CreateThread statement) are manupulating data that is on the original stack.
It would be great if you could point to me a scenario where this undefined behavior is prevented due to the fact that the two threads have their own stack.
Thanks
Vikram
OReubens
January 12th, 2005, 07:07 PM
The first code snippet has undefined behaviour. It may print 1 and then 2. Or it may print 2 twice.
Well, actually... It may also print 1 twice ;-) Depending on how it got compiled, and whether or not you have a 1CPU or MultiCPU machine.
scenario for this:
thread 1 reads *pI (being 0) in a register
thread 2 reads *pI (still 0) in a register
thread 1 adds 1 to register (resulting in 1)
thread 2 adds 1 to register (same register, but other instance), resulting in 1
Thread 1 stores register (1) to variable
Thread 2 stores register (1) to variable
Thread 1 outputs "1"
thread 2 outputs "1"
Although not so much an issue in this particular case, variables accessed 'unsafely' from multiple threads should be declared as volatile.
It would be great if you could point to me a scenario where this undefined behavior is prevented due to the fact that the two threads have their own stack.
Well, if the object is that you are indeed changing the same variable from the both threads (resulting in 1, 2)
The the most logical choice is to use InterlockedIncrement()
Solution:
main()
{
volatile long L=0;
HANDLE hT1 = CreateThread(NULL, 0, Fun1, &L, 0, NULL));
HANDLE hT2 = CreateThread(NULL, 0, Fun1, &L, 0, NULL));
CloseHandle(hT1); // You should always close thread handles when you no longer need them.
CloseHandle(hT2);
}
DWORD Fun1(void* p)
{
volatile long* pL = (volatile long*)p;
InterlockedIncrement(pL);
std:cout<<*pL;
return 0;
}
vikrampschauhan
January 13th, 2005, 02:04 AM
Hi OReubens,
Thanks for the elaborate explanation.
Please point me If I am wrong:
scenario for this:
thread 1 reads *pI (being 0) in a register
thread 2 reads *pI (still 0) in a register
thread 1 adds 1 to register (resulting in 1)
thread 2 adds 1 to register (same register, but other instance), resulting in 1
Thread 1 stores register (1) to variable
Thread 2 stores register (1) to variable --> So, the variable is overwritten here.
Thread 1 outputs "1"
thread 2 outputs "1"
This reminded me, whenever we say:
x=x+1;
x is loaded in the register, from the memory. It is incremented by one and then stored back at its location in the memory.
Another thing I wanted to ask you is about context switching. What all does a 'context' of a thread consist of?
When a thread is not being given any CPU time, it is put out of context. The second thread(thread2) is put in context. This means that the register values are restored to a state that they were, when the CPU was last serving thread 2. I believe only the register values are restored when a context switching occurs.
Am I correct?
Thanks
Vikram
OReubens
January 13th, 2005, 02:22 PM
Another thing I wanted to ask you is about context switching. What all does a 'context' of a thread consist of?
The context of a thread within a process consists of all of the CPU and FPU registers, the thread local storage and the thread's stack. Although both TLS and thread stack are a consequence of registers.
chkoo
January 14th, 2005, 01:33 AM
your example snippet is quite indicating the thesis of share resoure, critical section and atomic function.
In a process, context switching between threads is seemed to be no problem. Your snippet looks closer critical section of shared resource rather than this issue.
you must consider the atomic function of shared resource used in multiple threads in a process.
to get a predictablity result, you must use some protection technique(s) of critical section about shared resource. e.g. semaphore or just simple flag.
C. H. Koo
MrViggy
January 14th, 2005, 11:11 AM
your example snippet is quite indicating the thesis of share resoure, critical section and atomic function.
In a process, context switching between threads is seemed to be no problem. Your snippet looks closer critical section of shared resource rather than this issue.
you must consider the atomic function of shared resource used in multiple threads in a process.
to get a predictablity result, you must use some protection technique(s) of critical section about shared resource. e.g. semaphore or just simple flag.
C. H. Koo
Define "simple flag." If the simple flag is itself a variable, then it too will need some kind of access protection, no? ;)
Viggy
OReubens
January 14th, 2005, 01:54 PM
to get a predictablity result, you must use some protection technique(s) of critical section about shared resource. e.g. semaphore or just simple flag.
Regular flags (variables) are a very poor way to do synchronization. At best they can be used as one shot triggers (ex. a flag to indicate all threads should end so the app can quit), but any form of multi-usage flag is likely going to fail.
And there are more ways to properly synchronize than critical sections and semaphores... (mutex, event, waitable timers, I/O events, ...) And then there are advanced classes that combine any or more of these to achieve better results... CriticalSectionMaps, ReaderWriterLocks...
Define "simple flag." If the simple flag is itself a variable, then it too will need some kind of access protection, no?
Depends... One shot flags don't need synchronisation... consider a thread like this:
bool bEndThread = false;
DWORD ThreadProc(LPVOID lp)
{
while (!bEndThread)
{
// Do SomeStuff
}
return 0;
}
The ThreadProc (maybe even many of them) does work non-stop until the main GUI thread sets the bEndThreadFlag to true. No synchronisation is needed in this case.
MrViggy
January 14th, 2005, 03:21 PM
Okay, I'll buy that... Since only one thread is writing the flag, then things are cool. I was thinking of multiple threads using a flag to flag each other.
Viggy
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.