Visual Basic, .NET, ASP, VBScript
 

   
   
     

Форум - .NET

Страница: 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
  1.  
  2. Public Class Folders
  3.     Inherits List(Of Folder)
  4.  
  5.     'Ссылка на родителя.
  6.     Friend Owner As Folder
  7.  
  8.     Public Sub New(ByVal owner As Folder)
  9.         Me.Owner = owner
  10.     End Sub
  11.  
  12. End Class
  13.  
  14. Public Class Folder
  15.  
  16.     Private _Items As Folders
  17.     Private SyncRoot As Object = New Object
  18.  
  19.     ' 100% гарантия синхронизации. Но постоянные блокировки, даже при чтении свойства.
  20.     Public ReadOnly Property Items1 As Folders
  21.         Get
  22.             SyncLock(Me.SyncRoot)
  23.                 If Me._Items Is Nothing Then
  24.                     Me._Items = New Folders(Me)
  25.                 End If
  26.             End SyncLock
  27.             Return Me._Items
  28.         End Get
  29.     End Property
  30.  
  31.     ' Блокировка только при первом обращении. Но большая вероятность не атомарного выполнения конструктора.
  32.     ' И как следствие, другой поток может получить ссылку на _Items, которое уже не Nothing, но еще не полностью инициализировано.
  33.     Public ReadOnly Property Items2 As Folders
  34.         Get
  35.             If Me._Items Is Nothing Then
  36.                 SyncLock(Me.SyncRoot)
  37.                     If Me._Items Is Nothing Then
  38.                         Me._Items = New Folders(Me)
  39.                     End If
  40.                 End SyncLock
  41.             End If
  42.             Return Me._Items
  43.         End Get
  44.     End Property
  45.  
  46.     ' Тоже что и во втором варианте, тока инициализация _Items с помощью класса System.Threading.Interlocked
  47.     Public ReadOnly Property Items3 As Folders
  48.         Get
  49.             If Me._Items Is Nothing Then
  50.                 SyncLock(Me.SyncRoot)
  51.                     If Me._Items Is Nothing Then
  52.                         Dim instance As New Folders(Me)
  53.                         System.Threading.Interlocked.Exchange(Of Folders)(Me._Items, instance)
  54.                     End If
  55.                 End SyncLock
  56.             End If
  57.             Return Me._Items
  58.         End Get
  59.     End Property
  60.  
  61. End Class

Ответить

Номер ответа: 2
Автор ответа:
 Artyom



Разработчик

Вопросов: 130
Ответов: 6602
 Профиль | | #2 Добавлено: 17.09.10 15:39
# ' Блокировка только при первом обращении. Но большая вероятность не атомарного выполнения конструктора.
# ' И как следствие, другой поток может получить ссылку на _Items, которое уже не Nothing, но еще не полностью инициализировано.

Конструктор не атомарен, но присваивание атомарно. Ссылка на объект будет полчена когда он уже полностью проинициализирован (т.е. когда конструктор завершится), поэтому проблем не будет.

' Тоже что и во втором варианте, тока инициализация _Items с помощью класса System.Threading.Interlocked

Это лишнее.

Неатомарно может быть присваивание 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
Конструктор не атомарен, но присваивание атомарно. Ссылка на объект будет полчена когда он уже полностью проинициализирован (т.е. когда конструктор завершится), поэтому проблем не будет.
Как утверждал Artyom присваивание атомарно, но действительно ли это так (разная архитектура, система)? Тобишь операция Me._Items = instance выполниться атомарно. Или все же лучше использовать Interlocked.Exchange?

Ответить

Номер ответа: 9
Автор ответа:
 EROS



Вопросов: 58
Ответов: 4255
 Профиль | | #9 Добавлено: 17.09.10 17:43
Или все же лучше использовать Interlocked.Exchange?

Можешь использовать,страшного ничего не будет.. но Artyom прав.. это лишнее, поскольку операция присвоения ссылки атомарная..

Ответить

Номер ответа: 10
Автор ответа:
 Artyom



Разработчик

Вопросов: 130
Ответов: 6602
 Профиль | | #10 Добавлено: 18.09.10 04:49
Рассово верное решение на Lazy<T>
Кстати я ошибался, он на самом деле ещ ев 3.5 появился.

  1. class MyClass
  2. {
  3.     private Lazy<List<string>> _SomeItemsLazy;
  4.  
  5.     public MyClass()
  6.     {
  7.         _SomeItemsLazy = new Lazy<List<string>>(
  8.             () => Enumerable.Range(0, 10000000).Select(i => i.ToString()).ToList());
  9.     }
  10.  
  11.     public List<string> SomeItems
  12.     {
  13.         get
  14.         {
  15.             return _SomeItemsLazy.Value;
  16.         }
  17.     }
  18. }

Ответить

Номер ответа: 11
Автор ответа:
 BG(Алексей)



Вопросов: 26
Ответов: 295
 Профиль | | #11 Добавлено: 18.09.10 14:48
Кстати я ошибался, он на самом деле ещ ев 3.5 появился

Нет, 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 :)

Ответить

Страница: 1 |

Поиск по форуму



© Copyright 2002-2011 VBNet.RU | Пишите нам