Foxtable(狐表)用户栏目专家坐堂 → [求助]多线程加队列求助


  共有2671人关注过本帖树形打印复制链接

主题:[求助]多线程加队列求助

帅哥哟,离线,有人找我吗?
ajie5211
  1楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:四尾狐 帖子:820 积分:5641 威望:0 精华:1 注册:2015/11/28 14:00:00
[求助]多线程加队列求助  发帖心情 Post By: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


 回到顶部
帅哥哟,离线,有人找我吗?
ajie5211
  2楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:四尾狐 帖子:820 积分:5641 威望:0 精华:1 注册:2015/11/28 14:00:00
  发帖心情 Post By: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循环太快,上一个队列还没有执行完,自定义函数取订单后台信息:就把队列给弹出去了?不知道这里要如何修改哪?


 回到顶部
帅哥哟,离线,有人找我吗?
ajie5211
  3楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:四尾狐 帖子:820 积分:5641 威望:0 精华:1 注册:2015/11/28 14:00:00
  发帖心情 Post By: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


 回到顶部
帅哥哟,离线,有人找我吗?
ajie5211
  4楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:四尾狐 帖子:820 积分:5641 威望:0 精华:1 注册:2015/11/28 14:00:00
  发帖心情 Post By: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

怎么会有这个错误哪?


 回到顶部
帅哥哟,离线,有人找我吗?
ajie5211
  5楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:四尾狐 帖子:820 积分:5641 威望:0 精华:1 注册:2015/11/28 14:00:00
  发帖心情 Post By: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


 回到顶部
帅哥哟,离线,有人找我吗?
ajie5211
  6楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:四尾狐 帖子:820 积分:5641 威望:0 精华:1 注册:2015/11/28 14:00:00
  发帖心情 Post By:2018/1/19 9:50:00 [显示全部帖子]

我也很想上传实例,可这个例子我不知道怎么做,涉汲的东西太多了。后台是SQL的,还有视图,还用到了openQQ。

常出的报错是新增行时提示“索引超出范围。必须为非负值并小于集合大小。参数名: index”,就是5楼的错误。

信息更新不全常有发生,没有用多线程时,虽然所有人卡死,但信息没有漏掉的。信息不全这个,我还在观察到底可能是哪的问题。


 回到顶部
帅哥哟,离线,有人找我吗?
ajie5211
  7楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:四尾狐 帖子:820 积分:5641 威望:0 精华:1 注册:2015/11/28 14:00:00
  发帖心情 Post By:2018/1/24 13:52:00 [显示全部帖子]

多线程正在更新当前表数据时,如果双击单元格打开新的窗口,会报net内部错误,是因为更新当前表用的是主线程的原因吗?如何改比较好?

'委托执行函数(BaseMainForm应该是主线程的窗口,所以调用他用来在主线程中执行代码)
    BaseMainForm.Invoke(New _Delegate(AddressOf _MyDelegateMethod), "订单填写",New Object(){dr})


 回到顶部