Прочитав так много о том, как это сделать, я совершенно смущен.C# Обновление ссылок на объекты и многопоточность
Итак, вот что я хочу сделать: У меня есть структура данных/объект, который содержит все виды информации. Я надавливаю на структуру данных, как если бы она была неизменной. Всякий раз, когда мне нужно обновлять информацию, я делаю DeepCopy и вношу изменения в нее. Затем я заменю старый и вновь созданный объект.
Теперь я не знаю, как все делается правильно.
Давайте рассмотрим его со стороны читательских/потребительских потоков.
MyObj temp = dataSource;
var a = temp.a;
... // many instructions
var b = temp.b;
....
Как я понимаю, чтение ссылок является атомарным. Поэтому мне не нужна волатильность или блокировка, чтобы назначить текущую ссылку источника данных на temp. Но как насчет коллекции мусора. Насколько я понимаю, GC имеет своего рода счетчик ссылок, чтобы знать, когда освободить память. Поэтому, когда другой поток обновляет dataSource точно в тот момент, когда dataSource назначается temp, делает ли GC увеличение счетчика на правом блоке памяти? Другое дело в оптимизации компилятора/CLR. Я назначаю dataSource temp и использую temp для доступа к элементам данных. Что делает CLR? Это действительно делает копию dataSource или оптимизатор просто использует dataSource для доступа к .a и .b? Предположим, что между temp.a и temp.b указаны инструкции, так что ссылка на temp/dataSource не может храниться в регистре CPU. Так temp.b действительно temp.b или он оптимизирован для dataSource.b, потому что копирование в temp можно оптимизировать. Это особенно важно, если другой поток обновляет dataSource, чтобы указать на новый объект.
Действительно ли мне нужна изменчивость, блокировка, ReadWriterLockSlim, Thread.MemoryBarrier или что-то еще? Для меня важно то, что я хочу убедиться, что temp.a и temp.b обращаются к старой структуре данных, даже когда другой поток обновляет dataSource в другой вновь созданной структуре данных. Я никогда не изменяю данные внутри существующей структуры. Обновления всегда выполняются путем создания копии, обновления данных, а затем обновления ссылки на новую копию документа.
Возможно, еще один вопрос. Если я не использую volatile, сколько времени потребуется, пока все ядра всех процессоров не увидели обновленную ссылку?
Когда дело доходит до неустойчивого, пожалуйста, посмотри здесь: When should the volatile keyword be used in C#?
Я сделал небольшой тест программки:
namespace test1 {
public partial class Form1 : Form {
public Form1() { InitializeComponent(); }
Something sharedObj = new Something();
private void button1_Click(object sender, EventArgs e) {
Thread t = new Thread(Do); // Kick off a new thread
t.Start(); // running WriteY()
for (int i = 0; i < 1000; i++) {
Something reference = sharedObj;
int x = reference.x; // sharedObj.x;
System.Threading.Thread.Sleep(1);
int y = reference.y; // sharedObj.y;
if (x != y) {
button1.Text = x.ToString() + "/" + y.ToString();
Update();
}
}
}
private void Do() {
for (int i = 0; i < 1000000; i++) {
Something someNewObject = sharedObj.Clone(); // clone from immutable
someNewObject.Do();
sharedObj = someNewObject; // atomic
}
}
}
class Something {
public Something Clone() { return (Something)MemberwiseClone(); }
public void Do() { x++; System.Threading.Thread.Sleep(0); y++; }
public int x = 0;
public int y = 0;
}
}
В button1_Click есть для цикла и внутри цикла for я обращаюсь к объекту/объекту один раз с использованием прямого «shareObj» и после использования временно созданной «ссылки». Использовать ссылку достаточно, чтобы убедиться, что «var a» и «var b» инициализируются значениями из одного и того же объекта.
Единственное, что я не понимаю, это почему-то «Что-то ссылка = sharedObj;» не оптимизированы и «int x = reference.x;» не заменяется на "int x = sharedObj.x;"?
Как компилятор, дрожание знает, чтобы не оптимизировать это? Или временно объекты, никогда не оптимизированные на C#?
Но самое главное: мой пример работает по назначению, потому что он правильный или работает как только случайно?
Вам нужна какая-то синхронизация для общей переменной, которую вы мутируете. В противном случае записи, возможно, никогда не станут видимыми. летучесть была бы уместной. – usr
С новой информацией я не вижу никаких проблем. Когда вы меняете ссылку на объект и получаете доступ к ней до и после этого изменения/назначения, вы получите два разных результата. Volatile/MemoryBarrier - это совершенно другая вещь. Если вы используете локальную переменную temp или reference или что бы вы ни были хорошими. Но я не вижу причин для SO-сообщения. –
Причина проста. Я боюсь, что мой «экземпляр» ссылочного указателя может быть оптимизирован компилятором, даже если он теперь работает в режиме отладки и выпуска, я не знаю, будет ли он работать в будущем, когда джиттер может улучшиться и оптимизируется более агрессивно. Есть ли какая-либо документация о том, что C# Compiler и Jitter будет оптимизировать и что никогда не будет затронуто? Если у меня есть b = a; с = Ь; что мешает компилятору сделать это c = a и полностью удалить b? Вот почему я спрашиваю, я просто не знаю, как работают оптимизаторы. – bebo