|
Cool, Maxwell!
Here is the generated assembly code. Could you show a more practical sample or describe what is the purpose of MSDN sample please? Thanks.
1311006 add byte ptr [eax],al
int i;
for(i = 0; i < 10; i++)
{
if(v == 5)
{
break;
}
}
for(i = 0; i < 10; i++)
01311008 xor eax,eax
0131100A lea ebx,[ebx]
{
if(m == 3)
01311010 mov ecx,dword ptr [esp]
01311013 cmp ecx,ecx
01311015 je main+1Dh (131101Dh)
01311017 inc eax
01311018 cmp eax,0Ah
0131101B jl main+10h (1311010h)
{
break;
}
}
}
regards,
George
|
|
|
|
|
The example in MSDN is interesting! (My computer at work can not paste text in CP, but the one at home can. So I can copy and paste. )
Please read my comments in the code for details.
The result:
Critical Data = 1
Success
volatile bool Sentinel = true;
int CriticalData = 0;
unsigned ThreadFunc1( void* pArguments ) {
while (Sentinel) {
Sleep(0);
}
cout << "Critical Data = " << CriticalData << endl;
return 0;
}
unsigned ThreadFunc2( void* pArguments ) {
Sleep(2000);
CriticalData++;
Sentinel = false;
return 0;
}
int main() {
HANDLE hThread1, hThread2;
DWORD retCode;
hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc1,
NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc2,
NULL, 0, NULL);
if (hThread1 == NULL || hThread2 == NULL) {
cout << "CreateThread failed." << endl;
return 1;
}
retCode = WaitForSingleObject(hThread1,3000);
CloseHandle(hThread1);
CloseHandle(hThread2);
if (retCode == WAIT_OBJECT_0 && CriticalData == 1 )
cout << "Success" << endl;
else
cout << "Failure" << endl;
}
Maxwell Chen
|
|
|
|
|
Thanks Maxwell,
This is the code which makes me confused.
while (Sentinel)
{
Sleep(0);
}
I think when thread1 enters into the while loop, the value of Sentinel is true and it should invokes Sleep(0). Sleep(0) will wait until there is a signal. But in the program no other parties send thread1 a signal, and I think thread1 will wait forever to cause deadlock.
The only reason why there is no deadlock is when thread1 checks variable Sentinel, it happens to be false changed by thread2.
Anything wrong with my previous analysis? Please feel free to correct me.
regards,
George
|
|
|
|
|
Anything wrong with my previous analysis? Please feel free to correct me.
Yes, you're wrong in the following assumption:
George_George wrote: Sleep(0) will wait until there is a signal.
because MSDN http://msdn2.microsoft.com/en-us/library/ms686298.aspx[^] states:
Parameters
dwMilliseconds
The minimum time interval for which execution is to be suspended, in milliseconds.
A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution.
i.e. Sleeps(0) , in absence of signals, doesn't wait forever.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
You are correct. I think the behavior of thread1 is to continue to check the value of Sentinel again and again until it is false, right?
regards,
George
|
|
|
|
|
You hit the target, George.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
Now I strongly suspect whether compiler will generate any wrong code -- functional wrong code. In MSDN sample, variable Sentinel is used to act as a shared variable between thread1 and thread2. Compiler should guarantee that both threads can read/write the correct value of Sentinel.
It seems that volatile will make wrong optimization to prevent thread1 from reading the most recent correct value set by thread2? I think it will bring high risks to careless developers, who does not know about volatile and forget to put it ahead of the variable, which will result in the wrong optimization of compiler.
What is your perspective on such optimization?
regards,
George
|
|
|
|
|
No:
(1) thread safe code is up to the programmer.
(2) Optimizations usually undergo some hypothesis, if such hypothesis are broken then code functionality is broken. It is a price to pay to code speed.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
Then I am confused what is the original purpose of the MSDN sample? It advise people to put volatile keyword to all shared variables between multiple threads -- or else function may be broken?
In my past experience, I never use volatile to all threads' shared variables.
regards,
George
|
|
|
|
|
George_George wrote:
Then I am confused what is the original purpose of the MSDN sample? It advise people to put volatile keyword to all shared variables between multiple threads -- or else function may be broken?
That what an extreme sample. The shared variable was used as part of sinchronyzation mechanism (a spinlock) and some optimizations were turned on.
Usually we protect shared variables using some (OS provided) synchronization API, we don't do the opposite.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
I just want to confirm two things,
1. In your experience, when do you use volatile?
2. If we share data between threads and we are already using synchronization approach like mutex, do we need to add additional volatile keyword?
regards,
George
|
|
|
|
|
George_George wrote: 1. In your experience, when do you use volatile?
I never used it. (but I didn't write a lot of multithreaded code).
George_George wrote: If we share data between threads and we are already using synchronization approach like mutex, do we need to add additional volatile keyword?
IMHO no.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
I assume it is not much useful keyword and can be omitted.
regards,
George
|
|
|
|
|
Every keyword is useful. However, having a rather specialised pourpuse, you usually don't see programs with a lot of volatile s inside.
George_George wrote: and can be omitted.
You may omit it only whenever you don't need it (see, for instance, JudyL_FL
reply http://www.codeproject.com/script/Forums/View.aspx?fid=1647&msg=2372474[^]
)
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Yes, CPallini.
I agree it is needed. But currently I only see two cases which need this keyword,
1. Some hardware related, to let compiler read data from real memory other than from register;
2. Multi-threaded environment, I think volatile is only needed when we do not use other techniques, like critical section or mutex. If we use other techniques, volatile is not needed.
How do you think of my understanding?
regards,
George
|
|
|
|
|
In my opinion (I already said I'm not an expert about), your understanding is good.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
I personally prefer to use critical section or mutex, which has more control of concurrency behavior compared with volatile. Volatile only has very limited function in a multi-threaded environment to share data.
regards,
George
|
|
|
|
|
George_George wrote: 1. In your experience, when do you use volatile?
2. If we share data between threads and we are already using synchronization approach like mutex, do we need to add additional volatile keyword?
I don't use volatile in real-world code.
I started coding C/C++ in 1998. Only in the first year when I'd read volatile keyword, I tested it in exercise code. Well, at that time I did not know what debug/release builds were. I did not see the difference. Later on in 1999, I learned some more things, so I tried to use it. At that time, the threading design of mine was so poor. Yes, volatile was used in my early code, for once or twice. Finally I realized that with proper design for synchronisation mechanism of threading, volatile is not necessary.
Maxwell Chen
|
|
|
|
|
Maxwell Chen wrote: I don't use volatile in real-world code
I never did either when I was just writing for windows. An event serves the purpose of using a "flag" variable between threads. However, now that I'm writing cross-platform code, I find myself using "volatile flag" variables quite often for my multi-threaded stuff. The ease of use of the Windows event is not found on other platforms. I find it simpler to use the volatile keyword to prevent the compiler from optimizing away my variable rather than trying to find the best-for-each-platform way of implementing a simple signalling event between threads.
Judy
|
|
|
|
|
Hi Judy,
In your cross-platform development experience, I think you mean if we are not using volatile, the function of the program will change in your experience?
Could you show a more specific example please? I am interested in this since in my experience I have not found any case which behaves differently with/without volatile.
regards,
George
|
|
|
|
|
George_George wrote: In your cross-platform development experience, I think you mean if we are not using volatile, the function of the program will change in your experience?
yes, it happened to me in a VS2008 release mode version of a multi-threaded program
George_George wrote: Could you show a more specific example please? I am interested in this since in my experience I have not found any case which behaves differently with/without volatile
Sorry, I can't show you the specific chunk of code since it is proprietary, but the use of the volatile variable was very simple. A class created a worker thread during its processing. The worker thread was encapsulated in a class that contained a public boolean variable that was set to false in the constructor **. At some further point in its processing, independent of the thread, the class would change the value of the boolean (using its pointer to the thread class it had created) from false to true, indicating that the thread should stop what it was doing. The thread contained an infinite loop that checked the value of the boolean during its loop. When the boolean went true, the thread would terminate.
Without the use of volatile in the definition of the boolean within the thread class, the thread never terminated in release mode. When the code ran in debug mode, it worked correctly. When I examined the assembly language generated in release mode, I found that the compiler had optimized away the check of the boolean in the thread's loop.
If you are going to modify a variable used between threads, you should use volatile for safety's sake. Note that when I used Windows exclusively, I always used events for a simple "value change" flag between threads because it was guaranteed to be thread-safe. My worker threads don't usually directly affect data used by the initiating thread - they tend to communicate via windows messages (or the equivalent on other platforms) and pass data that way.
Your statement in other messages in this thread that you can omit the volatile keyword is not correct. Depending on what the optimizing compiler does, it can make a difference and this is the worst kind of bug to try and find. It all depends on what the optimizer does which changes every time you change the code.
Most developers don't encounter this kind of bug since this is not the best or safest way to share data between threads. I only encountered it when I started to "cheat" in my code because I didn't want to have to write platform-specific code every time I wanted to simply signal a thread.
Judy
** < edit >Thinking about it some more (since I don't have the code here with me at home), the variable was initializaed at the begining of the Run function which contains the infinite loop, not the constructor. Big difference since this way the optimizer sees the variable set to false and never changed, so therefore takes away the check of the variable inside the loop. < /edit >
modified on Saturday, December 29, 2007 2:42:20 AM
|
|
|
|
|
Thanks Judy,
Your sample is so great! I am wondering if you use mutex or other technologies, like critical section or something, to synchronize/share the cross-thread variable, do you think we still need the volatile keyword?
I have this concern is because some other people in this discussion mentioned that volatile is an alternative way to share data between threads (alternatives are like mutex), so if other approaches like critical section or mutex is used, volatile is no need.
How do you think of this idea?
regards,
George
|
|
|
|
|
Volatile has nothing to do with synchronization. It simply instructs the compiler that every access to the variable must be to the "real" memory allocated to that variable - it can't use registers to store the value, it can't not perform the memory access, and so on. Synchronization typically ensures that only one thread (or process) is using a resource at a time. My use of the volatile keyword does not force only one thread at a time to touch the variable. Technically, my example is "bad" code since it does not contain any logic to synchronize access to the variable. In my example, however, I can get away with it because of the way I use the variable. There is only one thread that changes the value and only one thread that checks the value. Also, the thread logic is such that if the thread loop runs one extra time due to a thread preemption (i.e. the changing thread sneaks in and changes the value after the checking thread has loaded the memory value but before it has checked the value), then there are no ill effects.
Things like mutexes and critical sections ensure single access to a resource. What that resource is has no relationship to the synchronization mechanism that protects it. The resource could be a shared memory location or a handle to a socket or a fifo or a pipe or ... or something as simple as a counter of the number of threads. To put it simply, the synchronization mechanism controls how the resource is protected and the resource is whatever you want it to be. The only relationship between the two is that the sync controls how many threads are allowed to touch the resource simultaneously.
Judy
|
|
|
|
|
I agree with your great analysis, Judy!
My concern is (maybe I have not made myself understood), from the MSDN sample, we can see that if we do not add volatile to the thread shared variables, the checking thread may deadlock -- I think it is what MSDN sample teaches us -- no volatile means no reliable and secure code.
But in the past the in many multi-threaded, I do not see they use volatile (they are using other approches like mutex). So, I am confused whether the their code is,
1. unsafe, and need to add volatile;
2. or safe, and mutex is an alternative way to tell compiler do not optimize (this is what I mean alternative).
Any comments? Please feel free to correct me if I am wrong.
regards,
George
|
|
|
|
|
George_George wrote: we can see that if we do not add volatile to the thread shared variables, the checking thread may deadlock -- I think it is what MSDN sample teaches us -- no volatile means no reliable and secure code.
I read that sample as saying that not using volatile means the code will not execute correctly since the compiler may mangle the code so that the variable does not operate as a mutex. Whether the code deadlocks or allows simultaneous access to the CriticalData variable depends on how the compiler optimizes the code.
George_George wrote: But in the past the in many multi-threaded, I do not see they use volatile (they are using other approches like mutex). So, I am confused whether the their code is,
1. unsafe, and need to add volatile;
2. or safe, and mutex is an alternative way to tell compiler do not optimize (this is what I mean alternative).
Most code doesn't use a combination of a volatile variable plus the Sleep to implement a mutex. Note that just using volatile does not turn the variable into a mutex - you must have the Sleep . Why go to that trouble when the OS has a mutex built in that does all that overhead stuff for you? The usual and "approved" approach is to use the functionality provided to you instead of rolling your own, unless you have a compelling reason to override the existing functionality. It's much safer to use the various sorts of synchronization mechanisms that all OSes provide rather than writing your own and possible not doing so correctly.
Use of a mutex, per se, does not tell the compiler not to optimize. The fact that you are making a function call limits the optimizer in what it can do to the code surrounding the function call.
Judy
|
|
|
|
|