不能从另一个线程访问 ConcurrentBag? [VB.NET]
ConcurrentBag is not accassible from another Thread? [VB.NET]
我正在寻找一个解决方案,我可以在其中存储我的自定义 Class 对象,并能够从多线程读取它。 (其他操作,如添加、删除可以从 GUI 完成,我只需要从列表中找到一个对象,然后调用它们的过程)。
我尝试了几种方法,但我总是以某种方式结束 NullReferenceException
。
我在 Form1 上的代码:
Friend conc As New Concurrent.ConcurrentBag(Of Item)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim thr As New Threading.Thread(AddressOf Feltolt)
thr.Start()
End Sub
Public Sub Feltolt()
For x As Integer = 0 To 100
conc.Add(New Item() With {.Value = x})
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim thr As New Threading.Thread(AddressOf Leker)
thr.Start()
End Sub
Private Sub Leker()
Dim d As New SeekerMeeker
MsgBox(d.GetValue(1))
End Sub
SeekerMeeker 代码class:
Public Function GetValue(val As Integer) As Integer
Return Form1.conc.SingleOrDefault(Function(t) t.Value = val).Value
End Function
但是它似乎“看不到”Form1,因为它是从线程访问的。有什么解决办法吗?
问题不在于您的 collection 不可访问。问题是您试图从不同于最初添加项目的 collection 获取项目。第二个代码片段是辅助线程上的 运行,它使用 Form1
的默认实例。默认实例是 thread-specific,因此您使用的 Form1
object 与您在屏幕上看到的不同,因此,Form1
object比包含您添加项目的 collection 的那个。
首先,您真的不应该直接从 SeekerMeeker
object 访问表单。那是糟糕的设计。如果你打算那样做,你需要实际将现有的 Form1
实例从外部传递到 object 并使用它,而不是试图在内部获取实例不同的线程。
要了解有关默认实例和表单间数据传递的更多信息,我建议您阅读我的博文 here and here。第二个包含三个部分,您应该阅读全部三个部分。最后一个才是正确的做事方式。
编辑:
该问题最明显的解决方案是在创建数据时将数据传递到需要它的 object 中。您可以像这样实现 SeekerMeeker
class:
Public Class SeekerMeeker
Private items As ConcurrentBag(Of Item)
Public Sub New(items As ConcurrentBag(Of Item))
Me.items = items
End Sub
'...
End Class
然后像这样在您的表单中创建一个实例:
Dim d As New SeekerMeeker(conc)
现在 SeekerMeeker
object 可以访问与表单相同的 ConcurrentBag
object,因此它将看到表单对其所做的任何更改。这就是您需要做的,即传递您需要的数据,而不是期望通过默认实例从空中提取它。
有经验的开发人员通常根本不使用默认实例。它们非常适合初学者和 VB6 开发人员。它们在 C# 中不存在,也没有人对此抱怨,因为它们不是必需的。只要遵循OOP的原则就可以了。
我正在寻找一个解决方案,我可以在其中存储我的自定义 Class 对象,并能够从多线程读取它。 (其他操作,如添加、删除可以从 GUI 完成,我只需要从列表中找到一个对象,然后调用它们的过程)。
我尝试了几种方法,但我总是以某种方式结束 NullReferenceException
。
我在 Form1 上的代码:
Friend conc As New Concurrent.ConcurrentBag(Of Item)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim thr As New Threading.Thread(AddressOf Feltolt)
thr.Start()
End Sub
Public Sub Feltolt()
For x As Integer = 0 To 100
conc.Add(New Item() With {.Value = x})
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim thr As New Threading.Thread(AddressOf Leker)
thr.Start()
End Sub
Private Sub Leker()
Dim d As New SeekerMeeker
MsgBox(d.GetValue(1))
End Sub
SeekerMeeker 代码class:
Public Function GetValue(val As Integer) As Integer
Return Form1.conc.SingleOrDefault(Function(t) t.Value = val).Value
End Function
但是它似乎“看不到”Form1,因为它是从线程访问的。有什么解决办法吗?
问题不在于您的 collection 不可访问。问题是您试图从不同于最初添加项目的 collection 获取项目。第二个代码片段是辅助线程上的 运行,它使用 Form1
的默认实例。默认实例是 thread-specific,因此您使用的 Form1
object 与您在屏幕上看到的不同,因此,Form1
object比包含您添加项目的 collection 的那个。
首先,您真的不应该直接从 SeekerMeeker
object 访问表单。那是糟糕的设计。如果你打算那样做,你需要实际将现有的 Form1
实例从外部传递到 object 并使用它,而不是试图在内部获取实例不同的线程。
要了解有关默认实例和表单间数据传递的更多信息,我建议您阅读我的博文 here and here。第二个包含三个部分,您应该阅读全部三个部分。最后一个才是正确的做事方式。
编辑:
该问题最明显的解决方案是在创建数据时将数据传递到需要它的 object 中。您可以像这样实现 SeekerMeeker
class:
Public Class SeekerMeeker
Private items As ConcurrentBag(Of Item)
Public Sub New(items As ConcurrentBag(Of Item))
Me.items = items
End Sub
'...
End Class
然后像这样在您的表单中创建一个实例:
Dim d As New SeekerMeeker(conc)
现在 SeekerMeeker
object 可以访问与表单相同的 ConcurrentBag
object,因此它将看到表单对其所做的任何更改。这就是您需要做的,即传递您需要的数据,而不是期望通过默认实例从空中提取它。
有经验的开发人员通常根本不使用默认实例。它们非常适合初学者和 VB6 开发人员。它们在 C# 中不存在,也没有人对此抱怨,因为它们不是必需的。只要遵循OOP的原则就可以了。