Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
static int mtx_locked = 0;
<source>
Это не работает:
www.nwcpp.org/Downloads/2004/DCLP_notes.pdf
Но в новом стандарте есть atomic int, для которого предоставляются дополнительные гарантии и его можно было бы использовать, если бы мьютексы не были бы futex'ами. Они уже используют атомарные операции и они и так быстро работают, если не возникает коллизии. Системный вызов используется только для ожидания освобождения futex'а.mtx.lock. К тому же вы меня не поняли — я имел ввиду совсем не DCLP. Переменная mtx_locked тут используется чтобы избежать синхронизации, если можно допустить, что при mtx_locked > 0 наш мютекс в локе, и мы делаем что-то другое.volatile указывает, что значение переменной может изменятся вне контекста текущей программы, например, средствами hardware.volatile переменная state в следующем примере выпиливается компилятором и выливается просто в while(true)...static volatile int state = 0;
void poll_state() {
while ( state == 0 ) {...};
}
volatile сильно влияет на производительность в худшую сторонуПосмотрите асм-код ниже — согласно ему (в этом конкретном частном случае) «static volatile» и «static» абсолютно одинаковы, а для «member» все, как ни странно, наоборот: они быстрее чем «member» на 8 тактов для каждого цикла (если не брать во внимание instruction-level parallelism и superscalar architecture).
//==============================
//static volatile int mtx_locked = 0;
//static int mtx_locked = 0;
...
// addr of mtx_locked -> ebx:
mov ebx, 0x00403220
jmp +0x05; // goto while;
// processNextRequest():
call processNextRequest()
// while ( mtx_locked || !mtx.lock(1) )
cmp dword ptr [ebx], 0x00
jnz -0x0a; // goto processNextRequest;
push 0x01; // time = 1
lea eax,[esp+0x04]; //mtx;
push eax;
call Mtx::lock(int) //mtx.lock(1);
add esp,0x08;
test eax, eax; //not locked ?
jz -0x1d; // goto processNextRequest;
// mtx_locked++;
inc dword ptr [ebx];
// processInLock():
call processInLock();
// mtx_locked--;
dec dword ptr [ebx];
// mtx.release():
push esp
call Mtx::release();
pop esp
...
//==============================
//class member = private: int mtx_locked;
...
// addr of this (eax) -> ebx:
mov ebx, eax
jmp +0x05; // goto while;
// processNextRequest():
call processNextRequest()
// while ( mtx_locked || !mtx.lock(1) )
cmp dword ptr [ebx+0x000002d0], 0x00
jnz -0x0e; // goto processNextRequest;
push 0x01; // time = 1
lea eax,[esp+0x04]; //mtx;
push eax;
call Mtx::lock(int) //mtx.lock(1);
add esp,0x08;
test eax, eax; //not locked ?
jz -0x21; // goto processNextRequest;
// mtx_locked++;
inc dword ptr [ebx+0x000002d0];
// processInLock():
call processInLock();
// mtx_locked--;
dec dword ptr [ebx+0x000002d0];
// mtx.release():
push esp
call Mtx::release();
pop ecx
...
Статик при добавлении volatile ожидаемо не показала никаких изменений в машинном коде.
std::mutex mtx;
void test_mutex() {
static int mtx_locked = 0;
while ( mtx_locked || !mtx.try_lock() ) {
// не могу блокировать - сделай что-то другое ... например попробуй снова
}
// за мютексом - блокирован ...
mtx_locked++;
// ничего не делаем
// unlock ...
mtx_locked--;
mtx.unlock();
}
4006e0: 8b 05 a2 09 20 00 mov 0x2009a2(%rip),%eax # 601088 <_ZZ10test_mutexvE10mtx_locked>
4006e6: 53 push %rbx
4006e7: bb 00 00 00 00 mov $0x0,%ebx
4006ec: 0f 1f 40 00 nopl 0x0(%rax)
4006f0: 85 c0 test %eax,%eax
4006f2: 75 fc jne 4006f0 <_Z10test_mutexv+0x10>
mtx_locked++ и mtx_locked-- лежат вообще в разных функциях.Но согласитесь такое происходит только если "// ничего не делаем".
А во вторых в реале mtx_locked++ и mtx_locked-- лежат вообще в разных функциях.
Может какие нибудь #прагмы использовать, что бы отключить синхронизацию?
Это абсолютно не важноНет это очень важно — компилятор никогда не выпилит ++ и — если между ними что-то находится.
Это очень плохо. Также очень плохо, что мьютекс и флаг — это два отдельных объектаОткуда вы это взяли — в статье это просто пример — в реале у меня такая и подобная логика естественно находится в одном классе…
Я уже раза три написал про atomic типы, введенные в C++11Вот именно что 11.
Нет это очень важно — компилятор никогда не выпилит ++ и — если между ними что-то находится.
mtx_locked++;
for (size_t i = 0; i < 100; ++i) {
sum += tst[i];
}
mtx_locked--;
Откуда вы это взяли — в статье это просто пример — в реале у меня такая и подобная логика естественно находится в одном классе…
Вот именно что 11.
Если компилятор увидит, что адрес локальной переменной никуда не передавалсяВот именно что локальной — а это к счастью не так.
Меня волнует, что люди, не знакомые с темой… Их ставят в пример.К сожалению вы правы — часто это так и есть. Хотя синхронизация, возможно, как раз та тема, где нужно вникать в любую мелочь — на одном копипасте здесь не вылезешь.
Люди… я показывал (пусть и на близких к си примерах) принцип работы, а не готовую реализацию…
Во первых, volatile в c++ указывает, что другой процесс (для библиотеки) или hardware изменяет переменную в памяти — прошу заметить не поток.
Достаточно интересная, но иногда неблагодарная работа — оптимизировать все это хозяйство.
Синхронизируя такой доступ, мы ограничеваем одновременное исполнение некоторых критичных участков кода. Как правило это один, редко несколько потоков (например 1 writer/ N readers).до боли все знакомо.
Необходимость синхронизации неоспорима. Чрезмерная же синхронизация очень вредна
Главное барьеры в памяти при этом проставить правильные ...
Немного о многопоточном программировании. Часть 1. Синхронизация зло или все-таки нет