Страница: 1 |
Страница: 1 |
Вопрос: Блокировка с двойной проверкой
Добавлено: 17.09.10 11:29
Автор вопроса: Андрей
Есть классическая древовидная структура, с «ленивой инициализацией». В однопоточной модели, проблем не. Но как быть с многопоточной моделью. Привожу 3 варианта решения проблемы: свойства Items1, Items2, Items3
Items1 - проблем нет, но постоянные блокировки.
Items2 - есть проблемы и то, что это плохой вариант, я знаю.
Items3 – интуитивно, проблем нету и блокировка только при первом обращении. Но это на интуитивном уровне. Хотелось бы услышать мнения гуру по этому варианту.
Public Class Folders
Inherits List(Of Folder)
Friend Owner As Folder
Public Sub New(ByVal owner As Folder)
Me.Owner = owner
End Sub
End Class
Public Class Folder
Private _Items As Folders
Private SyncRoot As Object = New Object
' 100% гарантия синхронизации. Но постоянные блокировки, даже при чтении свойства.
Public ReadOnly Property Items1 As Folders
Get
SyncLock(Me.SyncRoot)
If Me._Items Is Nothing Then
Me._Items = New Folders(Me)
End If
End SyncLock
Return Me._Items
End Get
End Property
' Блокировка только при первом обращении. Но большая вероятность не атомарного выполнения конструктора.
' И как следствие, другой поток может получить ссылку на _Items, которое уже не Nothing, но еще не полностью инициализировано.
Public ReadOnly Property Items2 As Folders
Get
If Me._Items Is Nothing Then
SyncLock(Me.SyncRoot)
If Me._Items Is Nothing Then
Me._Items = New Folders(Me)
End If
End SyncLock
End If
Return Me._Items
End Get
End Property
' Тоже что и во втором варианте, тока инициализация _Items с помощью класса System.Threading.Interlocked
Public ReadOnly Property Items3 As Folders
Get
If Me._Items Is Nothing Then
SyncLock(Me.SyncRoot)
If Me._Items Is Nothing Then
Dim instance As New Folders(Me)
System.Threading.Interlocked.Exchange(Of Folders)(Me._Items, instance)
End If
End SyncLock
End If
Return Me._Items
End Get
End Property
End Class
Ответы
Всего ответов: 14
Номер ответа: 1
Автор ответа:
Андрей
Вопросов: 1
Ответов: 5
Профиль | | #1
Добавлено: 17.09.10 11:32
Номер ответа: 2
Автор ответа:
Artyom
Разработчик
Вопросов: 130
Ответов: 6602
Профиль | | #2
Добавлено: 17.09.10 15:39
# ' И как следствие, другой поток может получить ссылку на _Items, которое уже не Nothing, но еще не полностью инициализировано.
Конструктор не атомарен, но присваивание атомарно. Ссылка на объект будет полчена когда он уже полностью проинициализирован (т.е. когда конструктор завершится), поэтому проблем не будет.
Это лишнее.
Неатомарно может быть присваивание Int64 (при сборке под x64 - уже атомарно). Тогда нужно делать обмен через Interlocked.Exchange.
Посмотри еще на Lazy<T>. Он появился в 4.0.
Номер ответа: 3
Автор ответа:
Андрей
Вопросов: 1
Ответов: 5
Профиль | | #3
Добавлено: 17.09.10 16:00
Если присваивание атомарно, для ссылочных типов, тогда зачем нужна перегрузка метода Interlocked.Exchange с параметрами ссылочного типа?
Например с свойством Items2 работают два потока A и B. Например поток В добрался до свойства первым и начал инициализацию _Items. Но не завершил ее до конца. Начал работу поток А он видит что _Items уже не Nothing и сразу попадает в строку Return Me._Items. Но ведь объект еще не готов. Во всяком случаи, об этом написано тут. http://ru.wikipedia.org/wiki/Double_checked_locking.
Если рассматривать свойство Items3 и то, что операция присваивания ссылки атомарна, то Interlocked.Exchange действительно лишнее. Но это в теории.
Номер ответа: 4
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #4
Добавлено: 17.09.10 16:45
MS рекомендует юзать модификатор volatile
Вот их рекомендации по реализации паттерна Singleton
http://msdn.microsoft.com/en-us/library/ms998558.aspx
Номер ответа: 5
Автор ответа:
Андрей
Вопросов: 1
Ответов: 5
Профиль | | #5
Добавлено: 17.09.10 16:53
паттерн Singleton применим к "ленивой инициализации" одного экземпляра, мне же надо реализовать для множества экземпляров.
Номер ответа: 6
Автор ответа:
Андрей
Вопросов: 1
Ответов: 5
Профиль | | #6
Добавлено: 17.09.10 16:55
в принципе, теоретически, обращение к свойству Items3 решает все проблемы. Вопрос был в следующем, действительно ли он решает все проблемы?
Номер ответа: 7
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #7
Добавлено: 17.09.10 17:08
я думаю да, проблем быть не должно..
Номер ответа: 8
Автор ответа:
Андрей
Вопросов: 1
Ответов: 5
Профиль | | #8
Добавлено: 17.09.10 17:24
Номер ответа: 9
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #9
Добавлено: 17.09.10 17:43
Можешь использовать,страшного ничего не будет.. но Artyom прав.. это лишнее, поскольку операция присвоения ссылки атомарная..
Номер ответа: 10
Автор ответа:
Artyom
Разработчик
Вопросов: 130
Ответов: 6602
Профиль | | #10
Добавлено: 18.09.10 04:49
Рассово верное решение на Lazy<T>
Кстати я ошибался, он на самом деле ещ ев 3.5 появился.
Номер ответа: 11
Автор ответа:
BG(Алексей)
Вопросов: 26
Ответов: 295
Профиль | | #11
Добавлено: 18.09.10 14:48
Нет, Artyom, нет его в 3,5.
Номер ответа: 12
Автор ответа:
Artyom
Разработчик
Вопросов: 130
Ответов: 6602
Профиль | | #12
Добавлено: 18.09.10 19:43
Есть, если верить MSDN, которой я имею основания верить
Номер ответа: 13
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #13
Добавлено: 18.09.10 20:41
Значит выкинь свою MSDN, потому что она тебе нагло врет..
Номер ответа: 14
Автор ответа:
Artyom
Разработчик
Вопросов: 130
Ответов: 6602
Профиль | | #14
Добавлено: 19.09.10 01:07
Виноват, не в тот раздел посмотрел.
В 3.5 этого класса нет, только в 4.0, впрочем это один из тысячи поводов переходить на 4.0