任务: 我有一个要求(用户可以一个接一个地连续保存而无需等待)所以我选择了多线程,我即将用Queue概念完成它。但我在一个场景中陷入困境。
关于代码的任务: 应用程序中有许多集合和对象,我为此创建了一个Queue类,并在其中创建了一个单独的集合和对象,并将所有这些集合和对象分配给队列类对象。每当用户保存更改时,所有这些集合和对象都被填充并分配给队列集合和对象并添加到队列中,因此后台工作者获取集合和对象并执行该过程。这样做是为了确保保存的值是正确的,因为有连续保存。
情境: 应用程序有一个网格,用户在其中选择一行并使用其中的唯一ID将值加载到集合中并绑定到表单。因此,用户将更新值并单击“保存”。然后他点击下一行,一旦加载了值,他再次进行更改并保存。这就是出现问题的地方。
由于我已将所有先前的集合分配给队列集合,当用户进行更改并单击“保存”并进入下一行并单击该集合时,应用程序中已存在的集合将重置(从为每个行选择重置集合以加载所选值)并加载新值。这使得更改反映了我在队列中的集合。因此,保存功能会受到影响。
现在我需要的是,即使收集现在已经重置,队列中的收集也不会受到影响。
我如何实现?
//代码:
的MainForm:
Public Class Form1
''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
// BackgroundWorkerClass:
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
答案 0 :(得分:1)
我遇到了同样的问题。我从下面的链接中得到了这个想法。
VB.Net Copy a list to store original values to be used later
'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
答案 1 :(得分:0)
首先,您的“Item”类应实现“System.ComponentModel.INotifyPropertyChanged”接口。这基本上是对类的更改将反映在您的网格中,而不必“重置”集合/网格。
其次,只发送需要保存到队列中的内容,而不是所有记录。
第三,当保存完成后,更新(如果需要)保存的“Item”类(一次一个属性),不要替换集合中的项目或类似的东西。
这可以帮助您指出正确的道路。当您遇到新问题时,请创建一个新问题。