如何从其他线程访问数据库信息? [英] How do I access database info from a different thread?

查看:93
本文介绍了如何从其他线程访问数据库信息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望伸出援助之手,也许是朝正确方向迈出的一步.

以下代码的问题是 SearchMyDBs 中的 returnTable 没有正确地将数据合并到表中,并且它返回为空.


为什么我在.Merge()中使用的DataTable在使用BackgroundWorker时其中有0行,而在将函数作为单线程应用程序调用时却已满?

我已经做了大量的阅读,但对于如何处理此代码以从其他线程获取主线程的数据表中的信息,我仍然有些困惑. (或使用其他方法?)

谢谢

I''m hoping for a helping hand, a point in the right direction perhaps.

The problem with the code below is the returnTable in SearchMyDBs does not correctly merge data into the table and it returns back empty.


Why does the DataTable I''m using in the .Merge() have 0 rows in it while using the BackgroundWorker, and yet it''s full when calling the function as a single-threaded application?

I''ve done a fair amount of reading and I''m still a little confused on how I should handle this code to get information in the datatables of the main thread from a different thread. (or maybe a different method to use?)

Thanks

Public Class DBHandle 'holds raw data
   'databases have been filled with data in this form
End Class

Public Class SearchMyDBs 'call for the searches
   Public Function GatherDataForViewer as DataTable
       Dim returnTable as new DataTable("Inventory")
       returnTable.Merge(DBHandle._InventoryDataSet.Inv02)
       'more data merges
       return returnTable
   End Function
End Class

Public Class ProjectViewer 'allow user to view data
   Dim _FullTable as datatable

   Public Sub LoadMe()
       'call initialize functions
       Me.Show()
       Me.BackgroundWorker.WorkerReportsProgress = True
       Me.BackgroundWorker.RunWorkerAsync()
   End Sub

   Private Sub BackgroundWorker1_DoWork(parameters) Handle
       _FullTable = SearchMyDBs.GatherDataForViewer
   End Sub

   Private Sub BackgroundWorker1_Completed(parameters) Handle
       Me.DataGrid.Datasource = _FullTable
   End Sub
End Class

推荐答案

我认为问题在于您有一个名为_FullTable的DataTable对象,该对象正在两个单独的线程上使用. BackgroundWorker_DoWork中的代码与其余代码在不同的线程上运行.您不应直接引用在原始线程中声明的变量.而不是访问_FullTable,您应该通过使用DoWorkEventArgs将表发送到BackgroundWorker_Completed方法(在原始线程上运行该方法).请参阅该
上的MSDN信息. 此处 [
I think the issue is that you have a DataTable object called _FullTable that you are using on two separate threads. The code in the BackgroundWorker_DoWork runs on a different thread from the rest of your code. You shouldn''t make references to variables that were declared in the original thread directly. Instead of accessing _FullTable you should send the table to the BackgroundWorker_Completed method (Which is running on the original thread) by making use of the DoWorkEventArgs. See the MSDN info on that
here[^].

Your code above doesn''t look complete, as you don''t show event argurments in your event handlers. It would look something like this:
Private Sub bgwSync_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwSync.DoWork
   e.Result = SearchMyDBs.GatherDataForViewer
End Sub

Private Sub bgwSync_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgwSync.RunWorkerCompleted
   _FullTable - CType(e.Result, DataTable)
End Sub



----更新-----
如果您确定.Merge可以在没有线程的情况下正常工作,那么我看到的另一个可能的问题是您正在调用
Do_Work事件中的DBHandle._InventoryDataSet.Inv02. (我不是非常有信心这是您的问题,但是至少可以尝试一下).为了将某些东西传递给背景工作人员,您可以将.RunWorkerAsync方法与参数一起使用. (
MSDN for RunWorkerAsync [



---- UPDATE -----
If you''re sure the .Merge works without the threading, another possible issue that I see is that you''re calling
DBHandle._InventoryDataSet.Inv02 within the Do_Work event. (I''m not super confident that''s your issue, but you can give it a try at least). In order to pass something INTO a backgroundworker you use the .RunWorkerAsync method with an argument. (MSDN for RunWorkerAsync[^])

You would do something like this:

Public Class SearchMyDBs 'call for the searches
    'Add a parm to this function so you can pass in the inv02 table to use in the merge
    Public Function GatherDataForViewer(ByRef dt As DataTable) As DataTable
        Dim returnTable As New DataTable("Inventory")
        returnTable.Merge(dt)
        'more data merges
        Return returnTable
    End Function
End Class

Public Class ProjectViewer 'allow user to view data
    Dim _FullTable As DataTable

    Public Sub LoadMe()
        'call initialize functions
        Me.Show()
        Me.BackgroundWorker.WorkerReportsProgress = True
        Me.BackgroundWorker.RunWorkerAsync(DBHandle._InventoryDataSet.Inv02) 'Pass in the inv02 table as the argument
    End Sub

    Private Sub bgwSync_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwSync.DoWork
        'Pull the inv02 out of the argument to use in the GatherDataForViewer function
        Dim dtInv02 As DataTable = CType(e.Argument, DataTable)
        e.Result = SearchMyDBs.GatherDataForViewer(dtInv02)
    End Sub

    Private Sub bgwSync_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgwSync.RunWorkerCompleted
        _FullTable = CType(e.Result, DataTable)
    End Sub

End Class


回答您的问题我如何访问…":您应该了解线程在这方面没有区别.由于数据库访问的时间安排,在许多情况下,您实际上应该为数据库操作创建一个单独的线程.这不会造成任何问题.如果您遇到合并问题,则该问题与您正在运行的线程无关.



为了响应有关线程的后续讨论(请参见下文):

因此,BackgroundWorker是一种简化的即用型线程处理方式.它更适合临时任务.大多数数据库通信都与整个应用程序的运行时间有关.

您需要在同一线程中执行所有数据库操作(每个数据库服务器);并且该线程不应该是UI线程.最好是在应用程序的最开始处创建一个后台线程,并一直使用它,直到整个应用程序过程终止为止,而不是不时地创建线程.每次需要在同一台数据库服务器上工作时,都需要重用同一线程,并根据需要将数据(例如,命令或其他请求)馈送到该线程.

首先,应使用构造函数System.Threading.Thread.Thread创建此类线程.使用线程的最佳方法是线程包装器.我帮助封装线程数据,避免参数化的线程启动(由于所需的类型大小写,这是很糟糕的);因为您可以将实例(非静态)方法传递给线程构造函数,所以您传递了隐式"this"参数(对包装实例的引用),因此,提供了对所有包装成员的访问.您可以使用锁或其他线程同步原语,从其封装的线程和外部"线程(例如,UI线程)同步对这些成员的访问.也就是说,您将线程安全性集中在包装器中.太多的机会错过了这个机会.请在线程包装中查看我的帖子:
如何将ref参数传递给线程 [ ^ ],
启动后更改线程(生产者)的参数 [ ^ ](带锁的那个).

现在,您通过包装器将数据馈送到线程.在没有线程的工作的情况下,应将其保持在等待状态,以免浪费CPI,直到有数据馈送的某些事件将其唤醒.最好的方法是使用阻塞队列.它可以是数据元素(任务")的队列,但也可以是委托实例队列的事件(显然,是直接指示要执行的操作).这种队列的原理与应用于UI线程的线程间调用非常相似.请参阅我的提示和技巧文章,其中提供了完整的源代码和全面的代码示例:
用于线程通信和线程间调用的简单阻塞队列 [ ^ ].

您还应该能够从非UI线程通知UI线程,仅用于更新视图等.因此,您需要使用调用.请在详细解释的地方查看我过去的答案:
Control.Invoke()与Control.BeginInvoke() [ ^ ],
Treeview Scanner和MD5问题 [如何获取keydown事件在vb.net中的不同线程上操作 [启用禁用+多线程后控件事件不会触发 [ ^ ].

—SA
To answer your question "How do I access…": you should understand that there is no difference between threads in this respect. Due to the timing of database access, you should really create a separate thread for your database operations in many or most of the cases. This should not create any problems. If you have a problem of merging, it is not related to the thread you are running.



In response to follow-up discussion on threading(see below):

So, BackgroundWorker is a simplified ready-to-use way of threading; it''s more adequate to temporary task. Most database communication last about the run time of the whole application.

You need to do all database operations in the same thread (per database server); and this thread should not be the UI thread. Instead of creation of a thread from time to time, it''s the best to create a background thread in the very beginning of the application and use it all the time until the whole application process terminates. You need to reuse the same very thread each time you need to work with the same database server, feeding data (for example, command or other request) to the thread as it is needed.

First of all, such thread should be created using the constructor System.Threading.Thread.Thread. The best way to use the thread is a thread wrapper. I helps to encapsulate thread data, avoid parametrized thread start (which is bad due to required type case); as you can pass an instance (non-static) method to the thread constructor, you pass an implicit "this" parameter (a reference to the wrapper instance), and hence, provide the access to all wrapper member. You can synchronize access to those members from its encapsulated thread and "foreign" thread (say, a UI thread) using lock or other thread synchronization primitives. That is, you centralize thread-safety in a wrapper. Too many benefits to miss this opportunity. Please see my posts on thread wrapper:
How to pass ref parameter to the thread[^],
change paramters of thread (producer) after it started[^] (this one with lock).

Now, you feed data to the thread via the wrapper. In there is not a job for a thread, is should be kept in a wait state wasting CPI no time until waken up by some event with data feed. The best way to do that is using a blocking queue. It can be a queue of data element ("tasks") but it could be event a queue of delegate instances (semantically, direct instructions on what to do). The principles of such queue are very similar to inter-thread invocation applied to UI thread. Please see my Tips&Tricks article where I provide a complete source code with comprehensive code samples:
Simple Blocking Queue for Thread Communication and Inter-thread Invocation[^].

You also should be able to notify UI thread from you non-UI thread, just to update the views, etc. So, you need to use invocation. Please see my past answers where I explain it in detail:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

See also more references on threading:
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

—SA


这篇关于如何从其他线程访问数据库信息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆