以文本方式查看主题

-  Foxtable(狐表)  (http://foxtable.net/bbs/index.asp)
--  专家坐堂  (http://foxtable.net/bbs/list.asp?boardid=2)
----  [求助]多线程加队列求助  (http://foxtable.net/bbs/dispbbs.asp?boardid=2&id=113550)

--  作者:ajie5211
--  发布时间:2018/1/18 9:19:00
--  [求助]多线程加队列求助

查看了很多论坛资料,多线程一直有点迷茫。因历史原因,我们的订单数据非常零散,现在通过后台SQL视图的形式在读取最新订单数据,用openQQ实现数据的同步刷新。因为是后台视图,不能用普通的行重载,追加数据的方式来更新前台数据,用SQLfind找到最新后台数据,与现前台数据对比,更新或加载,发现一人保存时,其它人哪里更新前台会造成窗口假死,啥也操作不了,非常影响操作体验,思考原因,主要是ReceivedMessage 是一条一条执行的,与循环语句一样,这时反复的用SQLfind非常耗费资源,速度变慢。为解决这个问题,就想用多线程来解决这个问题,因为要更新前台表,看论坛一定要用队列。根据论坛例子,我写了如下语句,但发现有压入队列,但队列操作和订单填写没有执行,请帮助修改。

全局代码如下

\'*******订单计划一览表多线程************
Public Sub setqddhtxx(ByVal obj As Object)  \'取订单计划一览表后台信息
    functions. Execute("取订单后台信息", obj)
End Sub

Public _MyQueue  As Queue(Of System.Data.DataRow)   \'定义的表队列
Public _TPool As System.Threading.ThreadPool  \'定义的线程

\'定义一个方法,用于线程委托
\'此处可以传参,为了方便,没写.
Public Sub ddd(ByVal obj As Object)
Functions.Execute("队列操作")
End Sub

\'\'\' <summary>
\'\'\' 委托体
\'\'\' </summary>
\'\'\' <param name="FunName">自定义函数名</param>
\'\'\' <param name="obj">传入参数</param>
\'\'\' <param name="OutObj">返回值</param>
\'\'\' <remarks></remarks>
Public Delegate Function _Delegate(ByVal FunName As String,ByVal obj() As Object)  As Object

\'委托的处理方法
\'根据狐表的自定义函数,定义一个通用的委托方法,便于调用
Public Function _MyDelegateMethod(ByVal FunName As String, ByVal ParamArray obj() As Object) As Object
Return Foxtable.Functions.Execute(FunName, obj)
End Function
\'****************************

内部函数如下

取订单后台信息:

Dim xsdh As String = Args(0)
Dim dr As DataRow = DataTables("订单计划一览表后台").SQLFind("销售单号 = \'" & xsdh & "\'")
Dim htdr As System.Data.DataRow
\'对像压入队列
If _MyQueue IsNot Nothing Then
    _MyQueue.Clear   \'不为空,清空
Else
    _MyQueue = New Queue(of System.Data.DataRow)   \'为空,初始化
End If
If dr IsNot Nothing Then
    \'MessageBox.Show("有取后台")
    htdr = dr.baseRow
    _MyQueue.Enqueue(htdr)
    Vars("逻辑1") += 2
End If
htdr = Nothing
Return Nothing

队列操作:

If  Vars("逻辑1") > 0 AndAlso _MyQueue IsNot Nothing AndAlso  _MyQueue.Count > 0  Then
    Dim dr As System.Data.DataRow = _MyQueue.Dequeue   \'获取队列的第一个值
    \'执行自己的逻辑代码   
    MessageBox.Show("队列操作有")
    \'委托执行函数(BaseMainForm应该是主线程的窗口,所以调用他用来在主线程中执行代码)
    BaseMainForm.Invoke(New _Delegate(AddressOf _MyDelegateMethod), "订单填写",New Object(){dr})
         
    Threading.Thread.CurrentThread.Sleep(200) \'模拟事务的执行过程
    Vars("逻辑1") += 1   \'通知执行下一个任务
Else
    Vars("逻辑1") = -1
End If

订单填写:

Dim htdr As  System.Data.DataRow =Args(0)
Dim dr As DataRow = DataTables("订单计划一览表").Select("销售单号 = \'"& htdr("销售单号") &"\'")(0)
If dr IsNot Nothing Then
    MessageBox.Show("订单填写有")
    \'加载更新项
    Dim sfxgpd As Boolean = False
    For Each dc As DataCol In DataTables("订单计划一览表").DataCols
        Dim key As String = "订单计划一览表:" & dr("销售单号") & ":" & dc.Name
        If tbrk.Contains(key) = False Then \'如果本人之前已经编辑此行,则正常编辑
            If dc.IsDate Then
                If htdr(dc.Name) = #1/1/1900# Then
                    dr(dc.Name) = Nothing
                End If
            Else If dc.Name = "操作记录" Then
                If htdr.IsNull(dc.Name) = False Then
                    If dr.IsNull(dc.Name) Then
                        dr(dc.Name) = htdr(dc.Name)
                    Else
                        If dr(dc.Name) <> htdr(dc.Name) Then
                            dr(dc.Name) = dr(dc.Name) & htdr(dc.Name)
                        End If
                    End If
                End If
            Else
                If dr(dc.Name) <> htdr(dc.Name) Then
                    dr(dc.Name) = htdr(dc.Name)
                End If
            End If
        Else
            sfxgpd = True
        End If
    Next
    If sfxgpd = False Then
        dr.Save()
    End If
Else
    Dim ndr As DataRow = DataTables("订单计划一览表").AddNew() \'没有时追加行
    For Each dc As DataCol In DataTables("订单计划一览表").DataCols
        ndr(dc.Name) = htdr(dc.Name)
        If dc.IsDate Then
            If ndr(dc.Name) = #1/1/1900# Then
                ndr(dc.Name) = Nothing
            End If
        End If
    Next
    ndr.Save()
End If
Return Nothing

收到来自好友或服务器的信息时触发ReceivedMessage代码如下:

Dim msg As String = e.Message
If msg.StartsWith("U#") Then \'表示修改了某行
    Dim pts() As String = msg.Split("#")
    If pts.Length = 3 Then
        If DataTables.Contains(pts(1)) Then
            If pts(1) = "订单计划一览表" AndAlso Forms("订单计划一览表").Opened = True Then
                Dim t1 As Threading.Thread
                t1 = New Threading.Thread(AddressOf setqddhtxx)
                t1.Start(pts(2))

        end if

      end if

   end if

end if


--  作者:有点甜
--  发布时间:2018/1/18 9:36:00
--  

你都没有调用,压入队列以后,要弹出和执行操作的,如

 

If _MyQueue IsNot Nothing AndAlso  _MyQueue.Count > 0 Then
    Dim dr As System.Data.DataRow = _MyQueue.Dequeue
    e.Form.Controls("Label1").Text = dr("数量")
    dr = Nothing
End If

 

如果要循环执行,就这样调用

 

Vars("逻辑1") += 2
_TPool.QueueUserWorkItem(New System.Threading.WaitCallback (AddressOf ddd))


--  作者:ajie5211
--  发布时间:2018/1/18 11:15:00
--  
以下是引用有点甜在2018/1/18 9:36:00的发言:

你都没有调用,压入队列以后,要弹出和执行操作的,如

 

If _MyQueue IsNot Nothing AndAlso  _MyQueue.Count > 0 Then
    Dim dr As System.Data.DataRow = _MyQueue.Dequeue
    e.Form.Controls("Label1").Text = dr("数量")
    dr = Nothing
End If

 

如果要循环执行,就这样调用

 

Vars("逻辑1") += 2
_TPool.QueueUserWorkItem(New System.Threading.WaitCallback (AddressOf ddd))

版主别激动,这东西帮助里没有详细说明,又不是科班出身,这个真不懂,还希望把我这颗小草当大树抱。

现在发现收到多条更新信息时,有些条目没有更新过来,是不是ReceivedMessage循环太快,上一个队列还没有执行完,自定义函数取订单后台信息:就把队列给弹出去了?不知道这里要如何修改哪?


--  作者:有点甜
--  发布时间:2018/1/18 11:27:00
--  

 压入队列代码有错,不能清空和重新new,试试改成下面

 

Dim xsdh As String = Args(0)
Dim dr As DataRow = DataTables("订单计划一览表后台").SQLFind("销售单号 = \'" & xsdh & "\'")
Dim htdr As System.Data.DataRow
If dr IsNot Nothing Then
    \'MessageBox.Show("有取后台")
    htdr = dr.baseRow
    _MyQueue.Enqueue(htdr)
    Vars("逻辑1") += 2
End If
htdr = Nothing
Return Nothing


--  作者:ajie5211
--  发布时间:2018/1/18 12:00:00
--  

发现内部函数“订单填写”新增行时,总出错提示“索引超出范围。必须为非负值并小于集合大小。参数名: index”

Dim htdr As  System.Data.DataRow =Args(0)
Dim dr As DataRow = DataTables("订单计划一览表").Select("销售单号 = \'"& htdr("销售单号") &"\'")(0)
If dr IsNot Nothing Then
Else
    Dim ndr As DataRow = DataTables("订单计划一览表").AddNew() \'没有时追加行
    \'For Each dc As DataCol In DataTables("订单计划一览表").DataCols
    For i As Integer = 0 To DataTables("订单计划一览表").DataCols.Count - 1
        Dim dc As DataCol = DataTables("订单计划一览表").DataCols(i)
        If htdr.Isnull(dc.Name) Then
             ndr(dc.Name) = Nothing
        Else
             ndr(dc.Name) = htdr(dc.Name)
        End If
    Next
    ndr.Save()
    Dim wz As Integer = Tables("订单计划一览表").FindRow(ndr)
    Tables("订单计划一览表").AutoSizeRow(wz)
End If
Return Nothing


--  作者:有点甜
--  发布时间:2018/1/18 12:03:00
--  

删除这两句看看

 

Dim wz As Integer = Tables("订单计划一览表").FindRow(ndr)
Tables("订单计划一览表").AutoSizeRow(wz)


--  作者:ajie5211
--  发布时间:2018/1/18 16:15:00
--  

.NET Framework 版本:2.0.50727.5420
Foxtable 版本:2017.12.31.1
错误所在事件:自定义函数取订单后台信息
详细错误信息:
调用的目标发生了异常。
未将对象引用设置到对象的实例。

自定义函数取订单后台信息如下

Dim xsdh As String = Args(0)
Dim dr As DataRow = DataTables("订单计划一览表后台").SQLFind("销售单号 = \'" & xsdh & "\'")
Dim htdr As System.Data.DataRow
If dr IsNot Nothing Then
    htdr = dr.baseRow
    _MyQueue.Enqueue(htdr)
    Vars("逻辑1") += 2
    _TPool.QueueUserWorkItem(New System.Threading.WaitCallback (AddressOf ddd))
End If
htdr = Nothing
Return Nothing

怎么会有这个错误哪?


--  作者:有点甜
--  发布时间:2018/1/18 17:10:00
--  

删除代码

 

_TPool.QueueUserWorkItem(New System.Threading.WaitCallback (AddressOf ddd))


 

开启的代码,在afterOpenProject处理即可

 

_TPool.QueueUserWorkItem(New System.Threading.WaitCallback (AddressOf ddd))

[此贴子已经被作者于2018/1/18 17:09:55编辑过]

--  作者:ajie5211
--  发布时间:2018/1/19 9:32:00
--  

报错,或是一直更新不完全,发现队列操作中Threading.Thread.CurrentThread.Sleep(200)这个时间设置的不能短了,短了就容易报错或是信息更新不全,怀疑是上一条订单修改还没有完成,就开始下一个队列了,但是这个设置的太长了,又会更新的很慢。有没有什么办法,等上一条完成了,才开始下一条?

队列操作:

If  Vars("逻辑1") > 0 AndAlso _MyQueue IsNot Nothing AndAlso  _MyQueue.Count > 0  Then
    Dim dr As System.Data.DataRow = _MyQueue.Dequeue   \'获取队列的第一个值
    \'执行自己的逻辑代码   
    MessageBox.Show("队列操作有")
    \'委托执行函数(BaseMainForm应该是主线程的窗口,所以调用他用来在主线程中执行代码)
    BaseMainForm.Invoke(New _Delegate(AddressOf _MyDelegateMethod), "订单填写",New Object(){dr})
         
    Threading.Thread.CurrentThread.Sleep(200) \'模拟事务的执行过程
    Vars("逻辑1") += 1   \'通知执行下一个任务
Else
    Vars("逻辑1") = -1
End If


--  作者:有点甜
--  发布时间:2018/1/19 9:35:00
--  
报什么错?上传具体实例测试。