c#多线程数据库填充表

时间:2017-02-01 03:34:50

标签: c# database multithreading dataset task

我有一个小困境。我有一个函数,它使用自己的连接字符串(可以相同或不同)浏览数据集中的查询列表,并开始填充所有DataTable并返回填充了所有查询的填充DataSet。

目前,此过程是逐个完成的,因此,如果其中一个查询需要10分钟而其他3个查询每个花费2分钟,则运行时间将为16分钟。

我想知道在这种情况下是否可以使用多线程。这应该在单独的线程上调用FillTable,并且应该将运行时间缩短到10分钟。这些只是Fill DataTable调用(不会有任何更新或删除调用)。

这是我到目前为止所得到的:

    public void FillDataSet(ref DataSet Source)
    { 
        foreach (var r in Source.Tables["queries"].Rows)
        {
            string query = r["QueryStatement"].ToString();
            string qSource = r["QuerySource"].ToString();
            string tableName = r["TableName"].ToString();

            DBConnection db = new DBConnection();
            var TempSource = Source;
            taskConnection = Task.Factory.StartNew(() => callDB(db, query, tableName, ref TempSource));
            Source = TempSource;
        }
        Task.WaitAll(taskConnection); 
    }

    private void callDB(DBConnection db, string query, string tableName, ref DataSet Source)
    {
        using (var sql = new SqlConnection(db.ConnectionString))
        {
            sql.Open();
            using (var adp = new SqlDataAdapter(query, sql))
            {
                adp.SelectCommand.CommandTimeout = 0;
                adp.Fill(Source, tableName);
            }
        }
    }

我必须创建一个TempSource,因为lambda表达式不喜欢传入参数的ref(我不能改变它)。目前这不起作用,不确定我做错了什么。

1 个答案:

答案 0 :(得分:1)

这是您可以使用的基本样板。 填写我发表评论的位置:

// Set your connectionstring and execute the query and fill your data here

这是基本的 - 我使用线程而不是线程池,因为与完成的工作量相比,产生新线程的开销很小。你可以通过跟踪线程和使用线程信号等来扩展它,以实现更高级的行为。

此外,如果您想将任何额外参数传递给完成工作的代码段,请将这些参数添加到工作项定义类中。

注意:这不支持主RunParallel方法的多个并行执行,但您可以轻松扩展它以执行此操作。

public static class RunParallel
{
    const int NumThreadsToRunInParallel = 8;// Tune this for your DB server performance characteristics
    public static void FillDataSet(ref DataSet Source)
    {
        WorkItemDefinition Work;
        foreach (DataRow r in Source.Tables["queries"].Rows)
        {
            Work = new WorkItemDefinition();
            Work.Query = r["QueryStatement"].ToString();
            Work.QSource = r["QuerySource"].ToString();
            Work.TableName = r["TableName"].ToString();
            EnQueueWork(Work);
        }
        System.Threading.ThreadStart NewThreadStart;
        NewThreadStart = new System.Threading.ThreadStart(ProcessPendingWork);
        for (int I = 0; I < NumThreadsToRunInParallel; I ++)
        {
            System.Threading.Thread NewThread;
            NewThread = new System.Threading.Thread(NewThreadStart);
            //NewThread.IsBackground = true; //Do this if you want to allow the application to quit before these threads finish all their work and exit
            ThreadCounterInc();
            NewThread.Start();
        }
        while (ThreadCounterValue > 0)
        {
            System.Threading.Thread.Sleep(1000);
        }
    }

    private static void ProcessPendingWork()
    {
        try
        {
            WorkItemDefinition Work;
            Work = DeQueueWork();
            while (Work != null)
            {
                Work = DeQueueWork();
                DbConnection db = new OdbcConnection();
                // Set your connectionstring and execute the query and fill your data here
            }
        }
        finally
        {
            ThreadCounterDec();
        }
    }

    private static int ThreadCounter = 0;
    private static void ThreadCounterInc()
    {
        lock(SyncRoot)
        {
            ThreadCounter += 1;
        }
    }
    private static void ThreadCounterDec()
    {
        lock (SyncRoot)
        {
            ThreadCounter -= 1;
        }
    }
    private static int ThreadCounterValue
    {
        get
        {
            lock (SyncRoot)
            {
                return ThreadCounter;
            }
        }
    }

    private static object SyncRoot = new object();
    private static Queue<WorkItemDefinition> m_PendingWork = new Queue<WorkItemDefinition>();
    private static Queue<WorkItemDefinition> PendingWork
    {
        get
        {
            return m_PendingWork;
        }
    }

    private static WorkItemDefinition DeQueueWork()
    {
        lock (SyncRoot)
        {
            if (PendingWork.Count > 0) // Catch exception overhead is higher
            {
                return PendingWork.Dequeue();
            }
        }
        return null;
    }

    private static void EnQueueWork(WorkItemDefinition Work)
    {
        lock (SyncRoot)
        {
            PendingWork.Enqueue(Work);
        }
    }

    public class WorkItemDefinition
    {
        public string Query { get; set; }
        public string QSource { get; set; }
        public string TableName { get; set; }
    }
}