Как заставить этот код работать с многопоточностью
Я создаю оценочную доску KPI для нашего склада и столкнулся со следующей проблемой.
У меня есть пользовательский элемент управления под названием order tile, который имеет следующий код:
Public Class OrderTile Private _BorderBoxColor As Color Public Property BorderBoxColor() As Color Get Return _BorderBoxColor End Get Set _BorderBoxColor = Value ChangeBackColor_Threadsafe(_BorderBoxColor) End Set End Property Private _SaleOrder As String Public Property SalesOrder() As String Get Return _SaleOrder End Get Set _SaleOrder = Value UpdateText_Threadsafe(lblSODS, _SaleOrder) End Set End Property Private _Customer As String Public Property Customer() As String Get Return _Customer End Get Set _Customer = Value UpdateText_Threadsafe(lblCustomer, _Customer) End Set End Property Private _SalesValue As String Public Property SalesValue() As String Get Return _SalesValue End Get Set _SalesValue = Value UpdateText_Threadsafe(lblValue, _SalesValue) End Set End Property Private _CountdownTime As TimeSpan Public Property CountDownTime() As TimeSpan Get Return _CountdownTime End Get Set _CountdownTime = Value tmrCountdown.Interval = 500 tmrCountdown.Start() End Set End Property Private _TargetDT As Date Public Property TargetDT() As Date Get Return _TargetDT End Get Set _TargetDT = Value End Set End Property Private _Flash As Boolean Public Property Flash() As Boolean Get Return _Flash End Get Set _Flash = Value If _Flash = False Then tmrFlashBorder.Stop() 'Me.BackColor = _BorderBoxColor ChangeBackColor_Threadsafe(_BorderBoxColor) ElseIf _Flash = True Then tmrFlashBorder.Start() End If End Set End Property Private Sub tmrCountdown_Tick(sender As Object, e As EventArgs) Handles tmrCountdown.Tick Dim ts As TimeSpan = _TargetDT.Subtract(DateTime.Now) If ts.TotalMilliseconds > 0 Then 'lblCountDownTimer.Text = ts.ToString("mm\:ss") UpdateText_Threadsafe(lblCountDownTimer, ts.ToString("mm\:ss")) Else 'lblCountDownTimer.Text = "00:00" UpdateText_Threadsafe(lblCountDownTimer, "00:00") tmrCountdown.Stop() End If End Sub Private Sub tmrFlashBorder_Tick(sender As Object, e As EventArgs) Handles tmrFlashBorder.Tick If Me.BackColor = Color.White Then Me.BackColor = _BorderBoxColor ElseIf Me.BackColor = _BorderBoxColor Then Me.BackColor = Color.White End If End Sub Delegate Sub UpdateText_Delegate(ByVal Lable As DevExpress.XtraEditors.LabelControl, ByVal text As String) Private Sub UpdateText_Threadsafe(ByVal Lable As DevExpress.XtraEditors.LabelControl, ByVal text As String) If Lable.InvokeRequired Then Dim MyDelegate As New UpdateText_Delegate(AddressOf UpdateText_Threadsafe) Me.Invoke(MyDelegate, New Object() {Lable, text}) Else Lable.Text = text End If End Sub Delegate Sub ChangeBackColor_Delegate(ByVal BackColor As Color) Private Sub ChangeBackColor_Threadsafe(ByVal BackColor As Color) If Me.InvokeRequired Then Dim MyDelegate As New ChangeBackColor_Delegate(AddressOf ChangeBackColor_Threadsafe) Me.Invoke(MyDelegate, New Object() {Me, BackColor}) Else Me.BackColor = BackColor End If End Sub End Class
и вот код, который использует этот элемент управления. этот код вызывается из фонового рабочего DoWork.
Private Sub DrawTiles() Dim XSpace = 368, YSpace = 120, XMax = 3, YMax = 4 OrderTiles.DefaultView.Sort = "ID ASC" 'grpOrderTiles.Controls.Clear() ClearTiles_Treadsafe(grpOrderTiles) Dim TC = 0 Dim TileCount As Integer = OrderTiles.Rows.Count For i = 0 To TileCount - 1 TC = i + 1 Dim NewTile As New FDVICKPIScreenV4.OrderTile NewTile.Name = "OrderTile" & OrderTiles.Rows(i).Item(ColTileID).ToString NewTile.SalesOrder = OrderTiles.Rows(i).Item(ColTileSalesOrderNo).ToString NewTile.Customer = OrderTiles.Rows(i).Item(ColTileCustomer).ToString NewTile.SalesValue = OrderTiles.Rows(i).Item(ColTileSalesValue).ToString NewTile.BorderBoxColor = Color.FromName(OrderTiles.Rows(i).Item(ColTileBox).ToString) NewTile.CountDownTime = TimeSpan.FromMinutes(30) NewTile.Flash = OrderTiles.Rows(i).Item(ColTileFlash) NewTile.TargetDT = OrderTiles.Rows(i).Item(ColTileTargetDT) If TC = 1 Then NewTile.Location = New Point(13, 34) ElseIf TC > 1 AndAlso TC <= 5 Then NewTile.Location = New Point(13 + XSpace * i, 34) '34 + YSpace * i ElseIf TC > 5 AndAlso TC <= 10 Then NewTile.Location = New Point(13 + XSpace * (i - 5), 154) ElseIf TC > 10 AndAlso TC <= 15 Then NewTile.Location = New Point(13 + XSpace * (i - 10), 274) ElseIf TC > 15 AndAlso TC <= 20 Then NewTile.Location = New Point(13 + XSpace * (i - 15), 394) End If AddTile_Treadsafe(grpOrderTiles, NewTile) Next End Sub
Код работает нормально, когда я вызываю его все в одном потоке, но когда я вызываю его из фонового рабочего, то есть когда я получаю "исключение User-Unhandled System.NullReferenceException: 'ссылка на объект не установлена на экземпляр объекта.'
Dim NewTile As FDVICKPIScreenV4.OrderTileв подлодке DrawTiles.
Что я уже пробовал:
Я пытался реализовать SyncLock, но не смог заставить это работать. Я просто недостаточно знаю о многопоточности, чтобы решить эту проблему.
Вся помощь очень ценится.
CHill60
Вы не должны взаимодействовать с графическим интерфейсом из вашего рабочего потока. Ознакомьтесь с некоторыми статьями здесь о резьбе, там есть несколько хороших примеров, которые вы можете адаптировать
Ralf Meier
Как уже упоминалось ранее, графический интерфейс и Backgroundworker, задача или поток-это разные вещи.
Если вы сделаете свой рабочий поток безопасным, вы потеряете временные преимущества, которых вы пытаетесь достичь, потому что вы даете своему элементу управления (графическому интерфейсу) информацию, чтобы сделать что-то, когда рабочий уже приостановлен. Лучше всего провести эксперименты с нарезанием резьбы сначала в очень легком варианте ...