Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, karbofos42, Вы писали:
K>>Ну, второй и прочитал два раза и два раза получил разное значение. K>>К чему это приведёт — зависит от программы
S>Разное значение — т.к. второй поток изменил. Что странного?
Ничего странного. Был вопрос к чему это может привести.
Я напомнил базовую вещь. Если читающий код будет правильно с этой изменяемой переменной работать, то ничего не будет.
У меня так коллеги из-за LINQ удивлялись по незнанию.
Проверяли у IEnumerable Count() и дальше работали с ним как-будто там точно есть такое количество элементов и оно не изменится в процессе работы.
Теперь всегда ToList() пишут и "фиксируют" коллекцию
Re[5]: Доступ к локальной переменной из разных потоков
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, karbofos42, Вы писали:
S>>>Локальная не может быть volatile. K>>Осталось разобраться как она так шарится между потоками
S>И как? В чем может быть проблема?
Я для кого про sharplab писал?
Я вот даже не поленился и скопировал пример, получился такой код:
internal class Program
{
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
public bool b;
internal void <Main>b__0()
{
Thread.Sleep(5000);
b = false;
}
}
private static void Main(string[] args)
{
<>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
<>c__DisplayClass0_.b = true;
Thread thread = new Thread(new ThreadStart(<>c__DisplayClass0_.<Main>b__0));
thread.IsBackground = true;
thread.Start();
while (<>c__DisplayClass0_.b)
{
Console.WriteLine("b=" + <>c__DisplayClass0_.b);
Thread.Sleep(1000);
}
Console.WriteLine("b=" + <>c__DisplayClass0_.b);
}
}
Вопросы про "локальную" переменную и как она в лямбду попадает остались?
Re[3]: Доступ к локальной переменной из разных потоков
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, vmpire, Вы писали:
V>>Если переменная не volatile и нет лока вокруг, то чтение, вероятно, может быть соптимизировано и читающий поток не получит нового значения.
S>Локальная не может быть volatile.
V>>В этом примере после компиляции переменная уже не будет локальной, это будет член класса, который сгенерируется из захваченной переменной. V>>В релизной компиляции всё будет зависеть от настроек компиляции и внутренней логики оптимизатора. S>Попробуйте вспомнить, откуда вы об этому узнали. Просто по слухам или где-то в авторитетных источниках прочитали?
Где читал — не помню. Но решарпер это подтверждает.
V>>При компиляции наружного чтения переменной компилятор может и не догадаться, что переменная другого класса может меняться. S>И что? Создаст свою копию переменной, так что второй поток на нее влиять не будет?
Нет, просто при компиляции в нативный код не сгенерирует инструкцию чтения ячейки памяти. Вынесет чтение за цикл, так как в цикле она не меняется.
Вот тут хорошо расписано про переупорядочивание.
V>>Пытаться демонстрировать случай я не буду, но я бы не стал рисковать, делая такую синхронизацию потоков. S>А причем тут синхронизация?
При том, что у вас два зависящих потока, синхронизирующиеся через булевскую переменную.
Re: Доступ к локальной переменной из разных потоков
Здравствуйте, Shmj, Вы писали:
S>Есть локальная переменная bool-типа, которую изменяет один поток а читает другой. Какие могут быть проблемы?
делай сразу как положено и не отсвечивай как ты вообще .NET кодером робишь ?
Другим подходом является использование ключевого слова volatile, которым помечаются необходимые поля класса. Оно заставляет компилятор генерировать барьеры памяти при каждом чтении и записи в переменную, помеченной volatile. Данный подход хорош в том случае, когда у вас один поток, или одни потоки только читают, а другие только записывают. Если же вам необходимо читать и изменять в одном потоке, то стоит воспользоваться оператором lock.
Здравствуйте, Shmj, Вы писали:
S>Есть локальная переменная bool-типа, которую изменяет один поток а читает другой. Какие могут быть проблемы?
В принципе это будет работать. Дотнет не гарантирует атомарности и корректного разделение переменных и полей, но поле типа bool и целые, на практике, работают атомарно и корректно. Если ты используешь переменную просто для завершения потока — это прокатит и проблем не будет.
Надо еще понимать, что в данном примере создается класс замыкания и переменная "b" становится его полем.
Проблемы могут начаться, если нужна точная и быстрая реакция на изменение значения. Какое-то время разные процессоры будут иметь свои копии. И изменение переменной в одном потоке не сразу попадет в кэш другого. Но в твоем примере это не важно. Ну, выйдет второй поток не через 5 секунд, а через 5 целых и 1 сотую секунды. Ничего не изменится.
С другой стороны есть и явное преимущество — нет блокировки и мемори-барьеров, сбрасывающих кэш. Они все равно случатся рано или поздно, но лучше поздно, так как это не повлияет на производительность.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Доступ к локальной переменной из разных потоков
Здравствуйте, vmpire, Вы писали:
V>Если переменная не volatile и нет лока вокруг, то чтение, вероятно, может быть соптимизировано и читающий поток не получит нового значения.
На практике это не так и MS поддерживает это негласное соглашение, так как в ином случае куча программ перестанет работать. Это вам не плюсы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Доступ к локальной переменной из разных потоков
Здравствуйте, vmpire, Вы писали:
V>В этом примере после компиляции переменная уже не будет локальной, это будет член класса, который сгенерируется из захваченной переменной.
Да.
V>В релизной компиляции всё будет зависеть от настроек компиляции и внутренней логики оптимизатора.
Нет. Если перемеменная уходит в замыкание, то она уходит в замыкание. Не может другой поток без замыкания к ней доступ получить, так что в этом примере все убдет работать.
Ну, а реально локальные переменные не могут быть расширены между потоками без хакерских методов.
V>При компиляции наружного чтения переменной компилятор может и не догадаться, что переменная другого класса может меняться.
Компилятор не человек. Он догадайки не играет. У него набор оптимизаций (очень скудный для шарпа и чуть более расширенный для ИЛа). Если объект попадает в замыкание, то единсвенное что может статься — это замыкание может быть занлайнено. Но:
1. МС не умеет на сегодня инлайнить замыкания (вообще).
2. Если замыкание уходит в функцию которую нельзя инлайнить, а функция потока именно такая, то инлайнинг невозможен.
3. В МС знают о вольном использовании переменных между потоками и поддерживают этот инваринт между версиями фрэмворка.
Так что не фиг пугать людей. Работало, работает и работать будет.
V>Пытаться демонстрировать случай я не буду, но я бы не стал рисковать, делая такую синхронизацию потоков.
Здравствуйте, Pzz, Вы писали:
Pzz>Вплоть до переупорядочивания чтений/записей процессором (не компилятором, а именно процессором).
А где здесь вообще есть порядок?
Pzz>В твоем простом примере, конечно, такое вряд ли поймаешь — пока Sleep() отработает, все 100500 раз устаканится. Но в реальной жизни они будут, причем проявляться будут весьма неочевидным и невоспроизводимым образом.
Вот именно в его простом примере проблем нет! А в более сложном нужно смотреть отдельно.
Ну и не выдумывай. Процессор не может атомарные операции переупорядочивать. А чтение и запись була и интов в дотнете обеспечиваются атомарными.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Доступ к локальной переменной из разных потоков
Здравствуйте, Mr.Delphist, Вы писали:
MD>На прошлой неделе аккурат это самое огрёб, из-за материализации linq-последовательности другим потоком, нежели тот который порождает
Совсем не то, но так то да — и там, и там потоки.
Ну, надо же соображать, что делаешь? Одно дело положиться на булеву переменную, которая читается и пишется атомарно, а другое дело в другой поток ленивый объект передать, заполнение которого основано на не статических данных.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Доступ к локальной переменной из разных потоков
Здравствуйте, Shmj, Вы писали:
S>Много раз слышал, но теперь начинаю сомневаться — может просто кто-то сказал и все за ним начали повторять.
Да просто фобии и слепое копирование. Сто раз видел подобное в других областях. Куча идиотов реализуют Dispose-паттерн для объектов никогда не хранящих неуправляемых ресурсов и т.п. Просто делают все под копирку не осмысляя происходящего.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Доступ к локальной переменной из разных потоков
Здравствуйте, Shmj, Вы писали:
S>Разное значение — т.к. второй поток изменил. Что странного?
Ну, типа если ты ожидал, что всегда будет одно и то же число строк на экран выведено, то тебя это может удивить. Но если тебе нужно закончить поток после того как, скажем, пользователь нажал на кнопочку — это вполне нормальное решение. Тебе не трогает, что поток может поработать еще несколько миллисекунд. Тебе важен сам факт его окончания в приемлемое время. И это произойдет.
Но надо понимать, что все это работает на простых типах. Для сложных могут начаться проблемы атомарности и т.п. По этому в сложных случаях нужно как следует думать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Доступ к локальной переменной из разных потоков
Здравствуйте, netch80, Вы писали:
N>Пример ни о чём.
Нормальный пример. Вместо вывода на консоль можно влепить любой долго работающий код.
N>Зачем одна нить пишет эту переменную? N>Зачем другая нить её читает?
Чтобы прервать выполнение потока, ваш КО
Скажем в одном потоке ведутся расчеты, а в гуевом юзер нажал на кнопку "Прервать".
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Доступ к локальной переменной из разных потоков
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, netch80, Вы писали: N>>Пример ни о чём. VD>Нормальный пример. Вместо вывода на консоль можно влепить любой долго работающий код.
Ну влепил долго работающий код, и что?
N>>Зачем одна нить пишет эту переменную? N>>Зачем другая нить её читает? VD>Чтобы прервать выполнение потока, ваш КО
Это только из примера этого кода. А если не зацикливаться на нём?
VD>Скажем в одном потоке ведутся расчеты, а в гуевом юзер нажал на кнопку "Прервать".
OK, сработает. Для такой задачи проблем не будет. Значит ли это, что так будет всегда и везде?
UPD: Ну меня поняли, надеюсь. Тут нехорошо идти в обе стороны — и циклиться на экстремальных ситуациях, и считать, что всё всегда будет идеально. Пример ТС слишком узок, твои комментарии по деталям уместны, но не дают общей системы. А ТС явно нужно учить общее понимание проблемы, чтобы не писать дурные провокационные вопросы.
Здравствуйте, VladD2, Вы писали:
VD>Ну, надо же соображать, что делаешь? Одно дело положиться на булеву переменную, которая читается и пишется атомарно, а другое дело в другой поток ленивый объект передать, заполнение которого основано на не статических данных.
Так это ж классика — сначала асинхронности не было и в помине, код был сугубо линейным. Затем добавили ещё чуть чуть, затем через время ещё чуть-чуть — и вот оно. И само собой, это вовсе не три строчки, мяса там куда больше.