Re[4]: Доступ к локальной переменной из разных потоков
От: Shmj Ниоткуда  
Дата: 13.11.20 08:15
Оценка:
Здравствуйте, karbofos42, Вы писали:

S>>Локальная не может быть volatile.

K>Осталось разобраться как она так шарится между потоками

И как? В чем может быть проблема?
Re[4]: Доступ к локальной переменной из разных потоков
От: Shmj Ниоткуда  
Дата: 13.11.20 08:16
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Это не локальная переменная относительно функций потоков.


Хорошо. Какую проблему вы видите и какой вариант решения?
Re[4]: Доступ к локальной переменной из разных потоков
От: Shmj Ниоткуда  
Дата: 13.11.20 08:42
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Вплоть до переупорядочивания чтений/записей процессором (не компилятором, а именно процессором).


Был такой пример:


        static void Main(string[] args)
        {
            bool a;

            Thread t = new Thread(() =>
            {
                a = true;
                while (a)
                {
                }
            });
            t.Start();

            Thread.Sleep(1000);
            a = false;

            t.Join();
        }


Прога зависает... Но тут два потока и пишут и читают — если убрать из второго запись — то все ОК.
Re[5]: Доступ к локальной переменной из разных потоков
От: karbofos42 Россия  
Дата: 13.11.20 08:52
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, karbofos42, Вы писали:


K>>Ну, второй и прочитал два раза и два раза получил разное значение.

K>>К чему это приведёт — зависит от программы

S>Разное значение — т.к. второй поток изменил. Что странного?


Ничего странного. Был вопрос к чему это может привести.
Я напомнил базовую вещь. Если читающий код будет правильно с этой изменяемой переменной работать, то ничего не будет.
У меня так коллеги из-за LINQ удивлялись по незнанию.
Проверяли у IEnumerable Count() и дальше работали с ним как-будто там точно есть такое количество элементов и оно не изменится в процессе работы.
Теперь всегда ToList() пишут и "фиксируют" коллекцию
Re[5]: Доступ к локальной переменной из разных потоков
От: karbofos42 Россия  
Дата: 13.11.20 08:56
Оценка: 12 (1) +1
Здравствуйте, 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]: Доступ к локальной переменной из разных потоков
От: Jack128  
Дата: 13.11.20 08:57
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, vmpire, Вы писали:


V>>Если переменная не volatile и нет лока вокруг, то чтение, вероятно, может быть соптимизировано и читающий поток не получит нового значения.


S>Локальная не может быть volatile.


https://docs.microsoft.com/en-us/dotnet/api/system.threading.volatile.read?view=net-5.0
Re[5]: Доступ к локальной переменной из разных потоков
От: vmpire Россия  
Дата: 13.11.20 12:07
Оценка:
Здравствуйте, Shmj, Вы писали:


V>>В этом примере после компиляции переменная уже не будет локальной, это будет член класса, который сгенерируется из захваченной переменной.

V>>В релизной компиляции всё будет зависеть от настроек компиляции и внутренней логики оптимизатора.
S>Попробуйте вспомнить, откуда вы об этому узнали. Просто по слухам или где-то в авторитетных источниках прочитали?
Где читал — не помню. Но решарпер это подтверждает.

V>>При компиляции наружного чтения переменной компилятор может и не догадаться, что переменная другого класса может меняться.

S>И что? Создаст свою копию переменной, так что второй поток на нее влиять не будет?
Нет, просто при компиляции в нативный код не сгенерирует инструкцию чтения ячейки памяти. Вынесет чтение за цикл, так как в цикле она не меняется.
Вот тут хорошо расписано про переупорядочивание.


V>>Пытаться демонстрировать случай я не буду, но я бы не стал рисковать, делая такую синхронизацию потоков.

S>А причем тут синхронизация?
При том, что у вас два зависящих потока, синхронизирующиеся через булевскую переменную.
Re: Доступ к локальной переменной из разных потоков
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 16.11.20 05:24
Оценка: +1
Здравствуйте, Shmj, Вы писали:

S>Есть локальная переменная bool-типа, которую изменяет один поток а читает другой. Какие могут быть проблемы?


А есть программа, которая пишет "Hello world". Какие могут быть проблемы?

S>Пример:


Пример ни о чём.

Зачем одна нить пишет эту переменную?
Зачем другая нить её читает?

Ответь на эти вопросы, тогда будет о чём говорить.
The God is real, unless declared integer.
Re: Доступ к локальной переменной из разных потоков
От: xma  
Дата: 16.11.20 05:38
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Есть локальная переменная bool-типа, которую изменяет один поток а читает другой. Какие могут быть проблемы?


делай сразу как положено и не отсвечивай как ты вообще .NET кодером робишь ?

Другим подходом является использование ключевого слова volatile, которым помечаются необходимые поля класса. Оно заставляет компилятор генерировать барьеры памяти при каждом чтении и записи в переменную, помеченной volatile. Данный подход хорош в том случае, когда у вас один поток, или одни потоки только читают, а другие только записывают. Если же вам необходимо читать и изменять в одном потоке, то стоит воспользоваться оператором lock.


Основы многопоточности в .NET Framework
https://habr.com/ru/company/nix/blog/260745/
Re: Доступ к локальной переменной из разных потоков
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.11.20 14:28
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Есть локальная переменная bool-типа, которую изменяет один поток а читает другой. Какие могут быть проблемы?


В принципе это будет работать. Дотнет не гарантирует атомарности и корректного разделение переменных и полей, но поле типа bool и целые, на практике, работают атомарно и корректно. Если ты используешь переменную просто для завершения потока — это прокатит и проблем не будет.

Надо еще понимать, что в данном примере создается класс замыкания и переменная "b" становится его полем.

Проблемы могут начаться, если нужна точная и быстрая реакция на изменение значения. Какое-то время разные процессоры будут иметь свои копии. И изменение переменной в одном потоке не сразу попадет в кэш другого. Но в твоем примере это не важно. Ну, выйдет второй поток не через 5 секунд, а через 5 целых и 1 сотую секунды. Ничего не изменится.

С другой стороны есть и явное преимущество — нет блокировки и мемори-барьеров, сбрасывающих кэш. Они все равно случатся рано или поздно, но лучше поздно, так как это не повлияет на производительность.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Доступ к локальной переменной из разных потоков
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.11.20 14:30
Оценка:
Здравствуйте, vmpire, Вы писали:

V>Если переменная не volatile и нет лока вокруг, то чтение, вероятно, может быть соптимизировано и читающий поток не получит нового значения.


На практике это не так и MS поддерживает это негласное соглашение, так как в ином случае куча программ перестанет работать. Это вам не плюсы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Доступ к локальной переменной из разных потоков
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.11.20 14:37
Оценка:
Здравствуйте, vmpire, Вы писали:

V>В этом примере после компиляции переменная уже не будет локальной, это будет член класса, который сгенерируется из захваченной переменной.


Да.

V>В релизной компиляции всё будет зависеть от настроек компиляции и внутренней логики оптимизатора.


Нет. Если перемеменная уходит в замыкание, то она уходит в замыкание. Не может другой поток без замыкания к ней доступ получить, так что в этом примере все убдет работать.

Ну, а реально локальные переменные не могут быть расширены между потоками без хакерских методов.

V>При компиляции наружного чтения переменной компилятор может и не догадаться, что переменная другого класса может меняться.


Компилятор не человек. Он догадайки не играет. У него набор оптимизаций (очень скудный для шарпа и чуть более расширенный для ИЛа). Если объект попадает в замыкание, то единсвенное что может статься — это замыкание может быть занлайнено. Но:
1. МС не умеет на сегодня инлайнить замыкания (вообще).
2. Если замыкание уходит в функцию которую нельзя инлайнить, а функция потока именно такая, то инлайнинг невозможен.
3. В МС знают о вольном использовании переменных между потоками и поддерживают этот инваринт между версиями фрэмворка.

Так что не фиг пугать людей. Работало, работает и работать будет.

V>Пытаться демонстрировать случай я не буду, но я бы не стал рисковать, делая такую синхронизацию потоков.




https://www.youtube.com/watch?v=j978pwMZjmE
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Доступ к локальной переменной из разных потоков
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.11.20 14:40
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Вплоть до переупорядочивания чтений/записей процессором (не компилятором, а именно процессором).


А где здесь вообще есть порядок?

Pzz>В твоем простом примере, конечно, такое вряд ли поймаешь — пока Sleep() отработает, все 100500 раз устаканится. Но в реальной жизни они будут, причем проявляться будут весьма неочевидным и невоспроизводимым образом.


Вот именно в его простом примере проблем нет! А в более сложном нужно смотреть отдельно.

Ну и не выдумывай. Процессор не может атомарные операции переупорядочивать. А чтение и запись була и интов в дотнете обеспечиваются атомарными.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Доступ к локальной переменной из разных потоков
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.11.20 14:42
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Например тут:

И чё? Выйдет точно так же.

Ты же не полагаешься на точное время работы потока. А если важно именно время, то это не верный подход.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Доступ к локальной переменной из разных потоков
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.11.20 14:45
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>На прошлой неделе аккурат это самое огрёб, из-за материализации linq-последовательности другим потоком, нежели тот который порождает


Совсем не то, но так то да — и там, и там потоки.

Ну, надо же соображать, что делаешь? Одно дело положиться на булеву переменную, которая читается и пишется атомарно, а другое дело в другой поток ленивый объект передать, заполнение которого основано на не статических данных.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Доступ к локальной переменной из разных потоков
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.11.20 14:48
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Много раз слышал, но теперь начинаю сомневаться — может просто кто-то сказал и все за ним начали повторять.


Да просто фобии и слепое копирование. Сто раз видел подобное в других областях. Куча идиотов реализуют Dispose-паттерн для объектов никогда не хранящих неуправляемых ресурсов и т.п. Просто делают все под копирку не осмысляя происходящего.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Доступ к локальной переменной из разных потоков
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.11.20 14:50
Оценка: +1
Здравствуйте, Shmj, Вы писали:

S>Разное значение — т.к. второй поток изменил. Что странного?


Ну, типа если ты ожидал, что всегда будет одно и то же число строк на экран выведено, то тебя это может удивить. Но если тебе нужно закончить поток после того как, скажем, пользователь нажал на кнопочку — это вполне нормальное решение. Тебе не трогает, что поток может поработать еще несколько миллисекунд. Тебе важен сам факт его окончания в приемлемое время. И это произойдет.

Но надо понимать, что все это работает на простых типах. Для сложных могут начаться проблемы атомарности и т.п. По этому в сложных случаях нужно как следует думать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Доступ к локальной переменной из разных потоков
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.11.20 14:54
Оценка:
Здравствуйте, netch80, Вы писали:

N>Пример ни о чём.


Нормальный пример. Вместо вывода на консоль можно влепить любой долго работающий код.

N>Зачем одна нить пишет эту переменную?

N>Зачем другая нить её читает?

Чтобы прервать выполнение потока, ваш КО

Скажем в одном потоке ведутся расчеты, а в гуевом юзер нажал на кнопку "Прервать".
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Доступ к локальной переменной из разных потоков
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.11.20 15:26
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, netch80, Вы писали:

N>>Пример ни о чём.
VD>Нормальный пример. Вместо вывода на консоль можно влепить любой долго работающий код.

Ну влепил долго работающий код, и что?

N>>Зачем одна нить пишет эту переменную?

N>>Зачем другая нить её читает?
VD>Чтобы прервать выполнение потока, ваш КО

Это только из примера этого кода. А если не зацикливаться на нём?

VD>Скажем в одном потоке ведутся расчеты, а в гуевом юзер нажал на кнопку "Прервать".


OK, сработает. Для такой задачи проблем не будет. Значит ли это, что так будет всегда и везде?

UPD: Ну меня поняли, надеюсь. Тут нехорошо идти в обе стороны — и циклиться на экстремальных ситуациях, и считать, что всё всегда будет идеально. Пример ТС слишком узок, твои комментарии по деталям уместны, но не дают общей системы. А ТС явно нужно учить общее понимание проблемы, чтобы не писать дурные провокационные вопросы.
The God is real, unless declared integer.
Отредактировано 17.11.2020 17:47 netch80 . Предыдущая версия .
Re[6]: Доступ к локальной переменной из разных потоков
От: Mr.Delphist  
Дата: 17.11.20 18:04
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Ну, надо же соображать, что делаешь? Одно дело положиться на булеву переменную, которая читается и пишется атомарно, а другое дело в другой поток ленивый объект передать, заполнение которого основано на не статических данных.


Так это ж классика — сначала асинхронности не было и в помине, код был сугубо линейным. Затем добавили ещё чуть чуть, затем через время ещё чуть-чуть — и вот оно. И само собой, это вовсе не три строчки, мяса там куда больше.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.