|
|
От: |
okman
|
https://searchinform.ru/ |
| Дата: | 31.05.13 16:26 | ||
| Оценка: | |||
int Data;
bool fReady = false;
void thread_producer()
{
Data = 123; // Запись данных.
Ready = true; // Публикация данных.
}
void thread_consumer()
{
while (false == fReady); // Ожидание публикации данных.
assert(123 == Data); // Чтение данных.
}#include <Windows.h>
#include <process.h>
int Value = 0;
int Base = 0;
bool fReady = false;
unsigned int _stdcall Thread_Consumer(void *)
{
while (false == fReady);
if (1 != Value) throw;
return 0;
}
unsigned int _stdcall Thread_Producer(void *)
{
Value = Base + 1;
fReady = true;
return 0;
}
int main()
{
HANDLE hThreads[2];
hThreads[0] = (HANDLE)_beginthreadex(NULL, 0, Thread_Consumer, NULL, 0, NULL);
Sleep(1000);
hThreads[1] = (HANDLE)_beginthreadex(NULL, 0, Thread_Producer, NULL, 0, NULL);
WaitForMultipleObjects(2, &hThreads[0], TRUE, INFINITE);
CloseHandle(hThreads[0]);
CloseHandle(hThreads[1]);
return 0;
}?Thread_Consumer@@YAIPEAX@Z PROC ; Thread_Consumer, COMDAT
; 13 : {
$LN11:
sub rsp, 40 ; 00000028H
movzx eax, BYTE PTR ?fReady@@3_NA ; fReady
npad 5
$LL3@Thread_Con:
; 14 : while (false == fReady);
test al, al
je SHORT $LL3@Thread_Con
...?Thread_Producer@@YAIPEAX@Z PROC ; Thread_Producer, COMDAT
; 24 : Value = Base + 1;
mov eax, DWORD PTR ?Base@@3HA ; Base
; 25 : fReady = true;
mov BYTE PTR ?fReady@@3_NA, 1 ; fReady
inc eax
mov DWORD PTR ?Value@@3HA, eax ; Value
...Microsoft Specific
Objects declared as volatile are not used in certain optimizations because their values
can change at any time. The system always reads the current value of a volatile object at
the point it is requested, even if a previous instruction asked for a value from the
same object. Also, the value of the object is written immediately on assignment.
When optimizing, the compiler must maintain ordering among references to volatile objects
as well as references to other global objects. In particular,
A write to a volatile object (volatile write) has Release semantics; a reference to a
global or static object that occurs before a write to a volatile object in the instruction
sequence will occur before that volatile write in the compiled binary.
A read of a volatile object (volatile read) has Acquire semantics; a reference to a
global or static object that occurs after a read of volatile memory in the instruction
sequence will occur after that volatile read in the compiled binary.
Microsoft Specific
Forces reads and writes to memory to complete at the point of the call.
The _ReadBarrier, _WriteBarrier, and _ReadWriteBarrier functions help ensure the correct
operation of multithreaded programs that are optimized by the Visual C++ compiler.
A correctly optimized program yields the same results when it executes on multiple
threads as when it executes on a single thread.
The point in an application where a _ReadBarrier, _WriteBarrier, or _ReadWriteBarrier
function executes is called a memory barrier. A memory barrier can be for reads, writes,
or both.
An instruction that accesses a variable in memory might be deleted or moved across a memory
barrier as part of an optimization. Consequently, a thread might read an old value from a
global variable before another thread completes writing a new value to the variable, or
write a new value before another thread completes reading an old value from the variable.
To help ensure that the optimized program operates correctly, the _ReadWriteBarrier function
forces reads and writes to memory to complete at the point of the call. After the call,
other threads can access the memory without fear that the thread that made the call might
have a pending read or write to the memory. A memory barrier prevents the compiler from
optimizing memory accesses across the barrier, but enables the compiler to still optimize
instructions between barriers.
// ...
int volatile Value = 0;
int volatile Base = 0;
bool volatile fReady = false;
// ...// ...
unsigned int _stdcall Thread_Consumer(void *)
{
while (false == fReady)
{
_ReadWriteBarrier(); // Форсим обращение к памяти fReady.
}
if (1 != Value) throw;
return 0;
}
unsigned int _stdcall Thread_Producer(void *)
{
Value = Base + 1;
_ReadWriteBarrier(); // Сохраняем порядок записи в Value и в fReady.
fReady = true;
return 0;
}
// ...?Thread_Consumer@@YAIPEAX@Z PROC ; Thread_Consumer, COMDAT
; 13 : {
$LN11:
sub rsp, 40 ; 00000028H
$LL3@Thread_Con:
; 14 : while (false == fReady);
movzx eax, BYTE PTR ?fReady@@3_NC ; fReady
test al, al
je SHORT $LL3@Thread_Con
...?Thread_Producer@@YAIPEAX@Z PROC ; Thread_Producer, COMDAT
; 24 : Value = Base + 1;
mov eax, DWORD PTR ?Base@@3HC ; Base
inc eax
mov DWORD PTR ?Value@@3HC, eax ; Value
; 25 : fReady = true;
mov BYTE PTR ?fReady@@3_NC, 1 ; fReady
...// ...
std::queue<int> g_Queue;
unsigned int _stdcall Thread_Consumer(void *)
{
while (0 == g_Queue.size());
if (123 != g_Queue.front()) throw;
return 0;
}
unsigned int _stdcall Thread_Producer(void *)
{
g_Queue.push(123);
return 0;
}
// ...?Thread_Consumer@@YAIPEAX@Z PROC ; Thread_Consumer, COMDAT
; 12 : {
$LN29:
sub rsp, 72 ; 00000048H
mov rax, QWORD PTR ?g_Queue@@3V?$queue@HV?$deque@HV?$allocator@H@std@@@std@@@std@@A+32
npad 5
$LL3@Thread_Con:
; 13 : while (0 == g_Queue.size());
test rax, rax
je SHORT $LL3@Thread_Con
...// ...
unsigned int _stdcall Thread_Consumer(void *)
{
size_t Size;
do
{
Size = g_Queue.size();
_ReadWriteBarrier();
} while (0 == Size);
if (123 != g_Queue.front()) throw;
return 0;
}
// ...?Thread_Consumer@@YAIPEAX@Z PROC ; Thread_Consumer, COMDAT
; 12 : {
$LN30:
sub rsp, 72 ; 00000048H
$LL4@Thread_Con:
; 13 : size_t Size;
; 14 :
; 15 : do
; 16 : {
; 17 : Size = g_Queue.size();
mov rax, QWORD PTR ?g_Queue@@3V?$queue@HV?$deque@HV?$allocator@H@std@@@std@@@std@@A+32
; 18 : _ReadWriteBarrier();
; 19 : } while (0 == Size);
test rax, rax
je SHORT $LL4@Thread_Con
...Marking memory with a memory barrier is similar to marking memory with the
volatile (C++) keyword. However, a memory barrier is more efficient because reads and
writes are forced to complete at specific points in the program rather than globally.
The optimizations that can occur if a memory barrier is used cannot occur if the
variable is declared volatile.