不能从另一个线程访问 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的原则就可以了。