Я читал книгу Joe Duffy по Параллельному программированию. У меня есть вид академического вопроса о незапертой поточной обработке.
Во-первых: Я знаю, что незапертая поточная обработка чревата опасностью (если Вы не верите мне, считайте разделы в книге о модели памяти),
Тем не менее, у меня есть вопрос: предположите, что у меня есть класс с международным свойством на нем.
Значение, на которое ссылается это свойство, будет читаться очень часто несколькими потоками
Чрезвычайно редко, чтобы значение изменилось, и когда это сделает это будет единственный поток, который изменяет его.
Если это действительно изменяется, в то время как другая операция, которая использует его, находится в полете, никто не собирается потерять палец (первая вещь, которую любой использующий его делает скопировать его в локальную переменную),
Я мог использовать блокировки (или readerwriterlockslim для хранения чтений параллельными). Я мог отметить энергозависимую переменную (много примеров, где это сделано),
Однако даже энергозависимый может наложить хит производительности.
Что, если я использую VolatileWrite, когда он изменяется, и оставляют доступ нормальным для чтений. Что-то вроде этого:
public class MyClass
{
private int _TheProperty;
internal int TheProperty
{
get { return _TheProperty; }
set { System.Threading.Thread.VolatileWrite(ref _TheProperty, value); }
}
}
Я не думаю, что когда-либо пробовал бы это в реальной жизни, но мне любопытно на предмет ответа (больше, чем что-нибудь, как контрольная точка того, понимаю ли я материал модели памяти, который я читал).
Маркировка переменной как «волатильный» имеет два эффекта.
1) Читает и пишеты имеют приобретение и выпустить семантику, так что читает и пишет другие места памяти не будут «двигаться вперед и назад во времени» в отношении чтения и записи этой памяти. (Это упрощение, но вы берете мою точку зрения.)
2) Код, сгенерированный джиттером, не будет «кэш» значение, которое, кажется, логически не изменяется.
Если бы предыдущая точка актуальна в вашем сценарии, я не знаю; Вы описали только одно местоположение памяти. Важно ли, что важно, чтобы у вас был только волатильный пишет, но не волатильный читает, это то, что зависит от вас, чтобы решить.
Но мне кажется, что последняя точка вполне актуальна. Если у вас есть спиновой замок на ненулительной переменной:
while(this.prop == 0) {}
Джиттер находится в его правах, чтобы генерировать этот код, как если бы вы написали
if (this.prop == 0) { while (true) {} }
, действительно ли это так или нет, я не знаю, но Это имеет право на. Если то, что вы хотите, для кода на самом деле повторно проверить свойство на каждом переходе на петлю, маркируя его как волатильный - это правильный путь.
Вопрос в том, увидит ли поток чтения когда-либо изменение. Вопрос не только в том, увидит ли он сразу .
Честно говоря, я перестал пытаться понять волатильность - я знаю, что это не совсем то, о чем я думал... но я также знаю, что при отсутствии своего рода барьера памяти на потоке чтения, вы можете читать одни и те же старые данные вечно.
Этот шаблон я использую, когда к ситуации применяется шаблон "последний пишущий". Я использовал ключевое слово volatile
, но после того, как увидел этот паттерн в примере кода от Джеффри Рихтера, я начал его использовать.
При определении макета и представления в XML можно указать ширину макета и высоту представления, которое должно быть содержимым обтекания или родительским элементом заливки. Занять половину площади немного труднее, но если бы у вас было что-то, что вы хотели на другой половине вы могли бы сделать что-то вроде следующего.
<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView android:layout_height="fill_parent"
android:layout_width="0dp"
android:layout_weight="1"/>
<ImageView android:layout_height="fill_parent"
android:layout_width="0dp"
android:layout_weight="1"/>
</LinearLayout>
Придание двум вещам одинакового веса означает, что они растянутся, чтобы занять одну и ту же долю экрана. Для получения дополнительной информации о компоновках см. документы для разработчиков.
-121--738044-Это ошибка WinINet, поэтому связанное с ней сообщение находится на WinINet.dll. Вы просто должны сообщить об этом FormatMessage (), чтобы получить правильное сообщение:
FormatMessage(
// flags:
FORMAT_MESSAGE_ALLOCATE_BUFFER // allocate buffer (free with LocalFree())
| FORMAT_MESSAGE_IGNORE_INSERTS // don't process inserts
| FORMAT_MESSAGE_FROM_HMODULE, // retrieve message from specified DLL
// module to retrieve message text from
GetModuleHandle(_T("wininet.dll")),
// error code to look up
errCode,
// default language
0,
// address of location to hold pointer to allocated buffer
(LPTSTR)&lpMsgBuf,
// no minimum size
0,
// no arguments
NULL );
Это официально задокументировано на MSDN в разделе "Обработка ошибок" документации WinINet.
Обратите внимание, что можно снова добавить флаг FORMAT _ MESSAGE _ FROM _ SYSTEM
, если вы хотите использовать эту подпрограмму для ошибок, которые могут или могут не исходить из WinINet: с установленным флагом FormatMessage ()
будет возвращаться в таблицу системных сообщений, если ошибка не найдена в wininet.dll. Однако не снимают флаг FORMAT_MESSAGE_IGNORE_INSERTS .
Да, каждый процессор в конечном итоге увидит изменение адреса памяти. Даже без замков или барьеров памяти. Блокировки и барьеры просто гарантируют, что все это произошло в относительном заказе (w.r.t другие инструкции), так что это оказалось правильным для вашей программы.
Проблема не в когерентности кэша (я надеюсь, что книга Джо Даффи не совершит эту ошибку). Тайники остаются согласованными - просто это требует времени, и процессоры не спешат ждать, пока это произойдет - если вы не введете его в действие. Таким образом, процессор переходит к следующей команде, которая может или не может в конечном итоге произойти перед предыдущей (потому что каждая память для чтения/записи занимает разное количество времени. По иронии судьбы потому, что времени для процессоров, чтобы договориться о когерентности и т.д. - это приводит к тому, что некоторые cachelines быть согласованными быстрее, чем другие (т.е. в зависимости от того, была ли строка изменена, исключена, разделена или недействительна, требуется больше или меньше работы для перехода в необходимое состояние).)
Поэтому чтение может выглядеть старым или из устаревшего кэша, но на самом деле это произошло раньше, чем ожидалось (обычно из-за предварительного просмотра и предсказания ветвления). Когда он действительно был прочитан, кэш был когерентным, он только что изменился с тех пор. Значит, значение не было старым, когда вы его читали, но теперь оно вам нужно. Вы просто читаете его слишком рано.: - (
Или эквивалентно, он был написан позже, чем логика вашего кода думал, что он будет написан.
Или оба.
В любом случае, если бы это было C/C + +, даже без блокировок/барьеров, вы бы в конечном итоге получили обновленные значения. (обычно в течение нескольких сотен циклов, так как память занимает примерно столько времени).В C/C + + можно использовать volatile (слабый энергонезависимый поток), чтобы убедиться, что значение не было прочитано из регистра. (Теперь есть некогерентный кэш! т.е. регистры)
В C # я не знаю достаточно о CLR, чтобы знать, как долго значение может оставаться в регистре, или как гарантировать, что вы получите реальное перечитывание из памяти. Вы потеряли «слабую» волатильность.
Я бы заподозрил, что пока переменный доступ не будет полностью скомпилирован, у вас в конечном итоге закончатся регистры (x86 не так много, чтобы начать с) и вы перечитаете.
Но никаких гарантий, которые я вижу. Если вы могли бы ограничить ваше волатильное чтение до определенной точки в вашем коде, который был часто, но не слишком часто (то есть начало следующей задачи в цикле времени (things_to_do)), то это может быть лучшее, что вы можете сделать.
«Хит производительности» из волатильный
, потому что компилятор теперь генерирует код, чтобы на самом деле проверять значение вместо того, чтобы оптимизировать это - другими словами, вы имеют Эта производительность ударила независимо от того, что вы делаете.
Я опубликовал свой предыдущий ответ, прежде чем увидел ваш комментарий о том, что вывод является открытым текстом. Теперь попробуйте:
$(document).ready(function() {
$("#formID").submit(function () {
$(":text").each(function () {
var value = $(this).val();
var myname = $(this).attr('name');
var newValue = value + " \n";
var hid = '<input type="hidden" name="' + myname + '" value="' + newValue + '"/>';
$(this).removeAttr('name');
$("#formID").append(hid);
});
});
});
Как говорит Марк, символ разрыва строки не визуализируется как разрыв строки при просмотре в браузере (или клиент электронной почты), поэтому вместо добавления символа разрыва строки «\n »следует добавить < br/>
$("#formID").submit(function () {
$(":text").each(function () {
var value = $(this).val();
var newValue = value + '<br/>';
$(this).val(newValue);
});
})
-121--4950662- Этот стиль обеспечивает максимальную точность, доступную в ЛЮБОЙ архитектуре, при назначении значения PI.
-121--1018855- В C # тип int
безопасен для потоков.
Поскольку вы сказали, что только один поток записывает в него, вы никогда не должны спорить о том, что является правильным значением, и, пока вы кэшируете локальную копию, вы никогда не должны получить грязные данные.
Однако вы можете объявить его изменчивым , если обновление будет выполняться потоком ОС.
Также помните, что некоторые операции не являются атомарными и могут вызвать проблемы при наличии нескольких устройств записи. Например, несмотря на то, что тип bool
не повреждается при наличии нескольких устройств записи, оператор, подобный
a = !a;
, не является атомарным. Если одновременно считываются два потока, возникает условие гонки.
Для нормальных вещей (например, для устройств с отображением памяти), протоколы кэш-валюта, происходящие внутри/между процессорами CPU/CPU, существуют для того, чтобы разные потоки, разделяющие эту память, получили согласованное представление о вещах (т.е., если я изменю значение ячейки памяти в одном процессоре, то будет видно другим процессорам, у которых память в кэше). В связи с этим волатильность позволит гарантировать, что оптимизатор не оптимизирует удаленные обращения к памяти (которые все равно всегда проходят через кэш), скажем, чтением значения, кэшированного в регистре. Документация на C# на этот счет выглядит вполне понятной. Опять же, прикладной программист, как правило, сам не имеет дела с кэш-валютой.
Настоятельно рекомендую прочитать свободно доступную статью "Что каждый программист должен знать о памяти". Под капотом происходит много волшебства, которое в основном предотвращает выстрел себе в ногу.