简体   繁体   中英

Can't return DataTable with thread

I'm trying to return DataTable from another class using thread but the thread is not returning the DataTable. While not using the thread, it works fine.

public class reatail
{
 DataTable order_dt = new DataTable();
    public DataTable loadAllOrder()
    {

        OleDbConnection co = new OleDbConnection();
        co.ConnectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + sd + "bowoni.accdb";
        string loadAll = "select * from allorder";
        co.Open();
        OleDbCommand cc = new OleDbCommand(loadAll, co);
        OleDbDataAdapter ad = new OleDbDataAdapter(cc);
        ad.Fill(order_dt);
        return order_dt;
    }
}



public partial class RecieveOrder : Form
{

    DataTable dy = new DataTable();
    reatail r = new reatail();
    Thread t;
    public void storeToStock()
    {
        //DataTable dy = new DataTable();
        Thread th=new Thread(()=>dy=r.loadAllOrder());
        th.Start();
        foreach(DataRow row in dy.Rows)
        {
            MessageBox.Show(row[0].ToString());
        }
    }
}

You are trying to access the return value even before your thread has finished executing. You need to do th.Join(); before starting to iterate the rows in the table using foreach loop. Here is how you can do it:

public partial class RecieveOrder : Form
{

    DataTable dy = new DataTable();
    reatail r = new reatail();
    Thread t;
    public void storeToStock()
    {
        //DataTable dy = new DataTable();
        Thread th=new Thread(()=>dy=r.loadAllOrder());
        th.Start();
        //wait for the thread to finish its execution and get the data from backend DB.
        th.Join();
        //now iterate the rows retrieved from DB
        foreach(DataRow row in dy.Rows)
        {
            MessageBox.Show(row[0].ToString());
        }
    }
}

There are a few things going wrong in your code.

To start with the issue you're facing is that you're starting a thread, but instead of allowing it to finish you're immediately trying to get the result. You need to employ some mechanism to know when the thread is finished to be able to continue the work back on the UI thread. Using th.Join() isn't the best approach as it locks up the UI thread while the data table is loaded.

The easiest thing these days is to employ the Task Parallel Library (TPL) to pin up and manage your threads for you.

Before I show you how I just wanted to comment on a couple of other things.

In your reatail class you have the order_dt variable as a field and you instantiate it only once. This means that in every place that loadAllOrder is called you'll get a reference to the same table and that table will continue to get filled up with more and more duplicate records. You want to move the field inside the loadAllOrder method to prevent that.

Also, in RecieveOrder you declare dy by instantiated a new DataTable , but you then reassign that variable with your call to loadAllOrder . That's a small waste. It's much better to keep your code clean and avoid unnecessary objects being created.

So you're better off doing this:

public class reatail
{
    public DataTable loadAllOrder()
    {
        DataTable order_dt = new DataTable();
        OleDbConnection co = new OleDbConnection();
        co.ConnectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + sd + "bowoni.accdb";
        string loadAll = "select * from allorder";
        co.Open();
        OleDbCommand cc = new OleDbCommand(loadAll, co);
        OleDbDataAdapter ad = new OleDbDataAdapter(cc);
        ad.Fill(order_dt);
        return order_dt;
    }
}

public partial class RecieveOrder : Form
{
    DataTable dy;
    reatail r = new reatail();
    public void storeToStock()
    {
        Task
            .Run(() => r.loadAllOrder())
            .ContinueWith(t =>
            {
                dy = t.Result;
                foreach (DataRow row in dy.Rows)
                {
                    MessageBox.Show(row[0].ToString());
                }
            }, TaskScheduler.FromCurrentSynchronizationContext());
    }
}

This doesn't lock up the UI, yet allows the load to run in the background.

The alternative that might even be simpler is to use async / await . Your storeToStock could be as simple as:

public partial class RecieveOrder : Form
{
    DataTable dy;
    reatail r = new reatail();
    public async void storeToStock()
    {
        dy = await Task.Run(() => r.loadAllOrder());
        foreach (DataRow row in dy.Rows)
        {
            MessageBox.Show(row[0].ToString());
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM