1

任务: 我有一个要求(用户能够一个接一个地连续保存而无需等待),所以我选择了多线程,我正准备用队列概念来完成它。但我在一种情况下陷入困境。

关于代码的任务: 应用程序中有许多集合和对象,我为其创建了一个 Queue 类并在其中创建了单独的集合和对象,所有这些都分配给了队列类对象。每当用户保存更改时,所有这些集合和对象都被填充并分配给队列集合和对象并添加到队列中,因此后台工作人员获取集合和对象并执行该过程。这样做是为了确保保存的值是正确的,因为存在连续保存。

场景: 应用程序有一个网格,用户在其中选择一行并使用其中的唯一 ID 将值加载到集合中并绑定到表单。因此用户将更新这些值并单击保存。然后他单击下一行,一旦加载了值,他就会再次进行更改并保存。这就是问题出现的地方。

由于我已将所有先前的集合分配给队列集合,因此当用户进行更改并单击保存并转到下一行并单击该集合时,应用程序中已经存在的集合正在重置(因为集合是为每个行选择重置以加载所选值)并加载新值。这使得更改也反映了我在队列中的集合。因此,保存功能在两者之间受到影响。

现在我需要的是,即使存在的集合已经被重置,我在队列中的集合也不应该受到影响。

我怎样才能实现?

//代码:

主窗体:

公开课形式1

''Queue collection of a class
'Dim oQueueCollection As New Queue(Of MyItem)
''Backgroundworker instance
'Dim bw As BackgroundWorker = New BackgroundWorker()


'Backgroundworker instance
Dim m_oBackgroundWorker As BackgroundWorker = Nothing

'Queue collection of a class
Dim m_cQueueCollection As New Queue(Of BackgroundworkerController)

Dim m_cBackgroundworkerController As New BackgroundworkerController


Private Property Item As New Item

''' <summary>
''' Get or set the Item Pricing collection.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property ItemPricingCollection() As Collection(Of ItemPricing) = New Collection(Of ItemPricing)


Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    'Initialize the backgroud worker
    m_oBackgroundWorker = New BackgroundWorker()

    'Call the completed method once the thread completes it work
    m_oBackgroundWorker.WorkerReportsProgress = True


    'Create thread and continue with the process
    AddHandler m_oBackgroundWorker.DoWork, AddressOf bw_DoWork

    'Method called after the thread work completes
    AddHandler m_oBackgroundWorker.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted

End Sub

Private Sub SaveItem()
    'Shows the item that starts the save.
    MsgBox(m_cQueueCollection.First().Item.ItemNo)

    'Makes the thread to sleep for the delay(In between we can make the next save)--- Testing purpose only
    System.Threading.Thread.Sleep(13000)
End Sub

Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)

    'Calls the save method
    SaveItem()

    'Shows the saved/Completed item's No
    'This Id will be assinged to all the places in which the application needs the current itemno(PK).
    e.Result = m_cQueueCollection.First().Item.ItemNo & " is Completed"

    'Removes the Completed item in queue
    m_cQueueCollection.Dequeue()
End Sub

Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
    'shows the result
    MsgBox(e.Result)

    'Check the collection and disable the timer in order not to run un necessarily
    If (m_cQueueCollection.Count = 0) Then
        Timer1.Enabled = False
        Timer1.Stop()
    End If
End Sub


Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    'ThreadPoolTest()

    'Sets the class to a property
    'this is done in order to have multiple class/collections inside the queue for processing
    m_cBackgroundworkerController.Item = Me.Item
    m_cBackgroundworkerController.ItemPricingCollection = Me.ItemPricingCollection

    'I have trided
    'm_cBackgroundworkerController.Item = DirectCast(Me.Item.clone(), Item)


    'Adds the collection to the queue
    m_cQueueCollection.Enqueue(m_cBackgroundworkerController)

    'enables the timer
    Timer1.Enabled = True

End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    Me.Item = Nothing
    Me.ItemPricingCollection.Clear()
End Sub

'Checks the collection and background worker and start the thread process for every 1 second.
'If the thread is running it just exits.

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    If (m_cQueueCollection.Count > 0 AndAlso Not m_oBackgroundWorker.IsBusy) Then
        m_oBackgroundWorker.RunWorkerAsync()
    End If
End Sub

End Class

//BackgroundWorker类:

Public Class BackgroundworkerController

Implements IDisposable



Private Shared s_bDisposed As Boolean
''' <summary>
''' Get or set the item.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>  
Public Property Item() As Item = New Item



''' <summary>
''' Get or set the Item Pricing collection.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property ItemPricingCollection() As Collection(Of ItemPricing) = New Collection(Of ItemPricing)

End class

//其中一个类:

<Serializable()> _
Public Class Item
    Implements IDisposable

    Private m_sItemNo As String = ""
   Private sEnvironmentCode sItemNo As String = ""
    Private m_bIsChanged As Boolean
    Private m_bIsInDatabase As Boolean



'jw10 end new collections added

''' <summary>
''' Get or set the Item Number.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property ItemNo() As String

    Get
        Return Me.m_sItemNo
    End Get

    Set(ByVal value As String)
        If Not Me.IsLoading Then Me.IsChanged = True
        Me.m_sItemNo = value
    End Set

End Property

''' <summary>
''' Get or set the environment code.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property EnvironmentCode() As String

    Get
        Return Me.m_sEnvironmentCode
    End Get

    Set(ByVal value As String)
        If Me.m_sEnvironmentCode <> value Then Me.m_bIsChanged = True
        Me.m_sEnvironmentCode = value
    End Set

End Property



''' <summary>
''' Get or set the changed flag.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property IsChanged() As Boolean

    Get
        Return Me.m_bIsChanged
    End Get

    Set(ByVal Value As Boolean)
        Me.m_bIsChanged = Value
    End Set

End Property



''' <summary>
''' Get or set the is in database flag.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property IsInDatabase() As Boolean

    Get
        Return Me.m_bIsInDatabase
    End Get

    Set(ByVal Value As Boolean)
        Me.m_bIsInDatabase = Value
    End Set

End Property

Public Overloads Sub Dispose() Implements IDisposable.Dispose

    'Check to see if dispose has already been called
    If Not s_bDisposed Then

        'Call the dispose method 
        Me.Dispose(True)

        'Tell the garbage collector that the object doesn't require cleanup 
        GC.SuppressFinalize(Me)

    End If

End Sub

Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)

    'Flag class as disposed 
    s_bDisposed = True

End Sub

End Class
4

2 回答 2

1

我也面临同样的问题。我从下面的链接中得到了这个想法。

VB.Net 复制一个列表以存储原始值以供以后使用

试试下面的代码

'Sets the class to a property
'this is done in order to have multiple class/collections inside the queue for processing
m_cBackgroundworkerController.Item = CType(DeepCopy(Me.Item), Item)
m_cBackgroundworkerController.ItemPricingCollection = CType(DeepCopy(Me.ItemPricingCollection), Collection(Of ItemPricing))


'Adds the collection to the queue
m_cQueueCollection.Enqueue(m_cBackgroundworkerController)

'enables the timer
Timer1.Enabled = True




Public Function DeepCopy(ByVal ObjectToCopy As Object) As Object

    Using mem as New MemoryStream

        Dim bf As New BinaryFormatter
        bf.Serialize(mem, ObjectToCopy)

        mem.Seek(0, SeekOrigin.Begin)

        Return bf.Deserialize(mem)

    End Using

End Function
于 2013-09-30T11:54:02.053 回答
0

首先,您的“Item”类应该实现“System.ComponentModel.INotifyPropertyChanged”接口。这基本上是这样对类所做的更改将反映在您的网格中,而无需“重置”集合/网格。

其次,仅将需要保存的内容发送到您的队列,而不是所有记录。

第三,保存完成后,更新(如果需要)保存的“项目”类(一次一个属性),不要替换集合中的项目或类似的东西。

这应该可以帮助您指出正确的道路。当您遇到新问题时,请创建一个新问题。

于 2013-09-27T20:49:54.343 回答