volatile для ссылочного типа в .net 4.0 - PullRequest
8 голосов
/ 29 декабря 2011

Я запутался в volatile для ссылочного типа.

Я понимаю, что для примитивного типа, volatile может немедленно отражать изменения значений из другого потока. Для ссылочного типа он может отражать изменения адреса немедленно. Тем не менее, как насчет содержания объекта. Они все еще кешируются?

(Предполагается, что List.Add() является атомарной операцией)

Например, у меня есть:

class A
{
     volatile List<String> list;
     void AddValue()
     {
        list.Add("a value");
     }

}

Если один поток вызывает функцию AddValue, адрес списка не изменяется, будет ли другой поток получать информацию об изменении содержимого в списке, или содержимое может кэшироваться для каждого потока, и это не обновление для других тем?

Ответы [ 4 ]

20 голосов
/ 29 декабря 2011

Я понимаю, что для примитивного типа volatile может сразу же отражать изменения значений из другого потока

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

Прежде всего, будьте уверены, что volatile влияет переменные , а не значения .

Во-вторых, volatile не влияет на переменные, содержащие значения типов значений, иначе, чем на переменные, содержащие ссылки.

В-третьих, volatile не означает, что изменения значений из других потоков видны сразу .Volatile означает, что переменные имеют семантику получения и выпуска .Volatile влияет на порядок, в котором могут наблюдаться побочные эффекты мутаций памяти, происходящих из определенного потока .Идея о том, что существует последовательный универсальный порядок мутаций и что эти мутации в этом порядке можно наблюдать мгновенно из всех потоков, не является гарантией, сделанной моделью памяти.

Однако как насчет содержимого объекта?

А как насчет этого?Место хранения, на которое ссылается переменная переменного типа ссылки, не должно иметь каких-либо особых характеристик потоков.

Если один поток вызывает функцию AddValue, адрес списка не изменится, обновится ли другой потокоб "содержательном" изменении списка.

Нет.С чего бы это?Этот другой поток может быть на другом процессоре, и этот кэш процессора, возможно, предварительно загрузил страницу, содержащую адрес массива, который поддерживает список.Отключение списка могло изменить местоположение хранилища, содержащее адрес массива, чтобы ссылаться на какое-то совершенно другое местоположение.

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

Вам не нужен volatile;все, что вам нужно, это установить блокировку потоков вокруг доступа к списку.Поскольку блокировки нитей создают полные заборы, вам не нужно использовать половину заборов, введенных volatile.

6 голосов
/ 29 декабря 2011

Это хуже, чем это.

Если вы одновременно обращаетесь к объекту, который не является потокобезопасным, ваша программа может фактически аварийно завершить работу. Получение устаревшей информации не является худшим потенциальным результатом.

При совместном использовании объектов библиотеки базовых классов .NET между потоками у вас действительно нет другого выбора, кроме как использовать блокировку. Для программирования без блокировки вам нужны инвазивные изменения в ваших структурах данных на самых низких уровнях.

2 голосов
/ 30 декабря 2011

Посмотрите на http://www.albahari.com/threading/part4.aspx#_The_volatile_keyword для хорошего объяснения того, что на самом деле делает volatile и как оно влияет на поля.

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

2 голосов
/ 29 декабря 2011

Ключевое слово volatile не влияет на содержимое списка (или, точнее, на объект, на который ссылается).

Говоря о updated / не обновляется для другого потока - это упрощение того, что происходит.Вы должны использовать оператор lock для синхронизации доступа к общему списку.В противном случае вы фактически столкнетесь с условиями гонки, которые могут привести к сбою программы.Класс List<T> сам по себе не является потокобезопасным.

...