我有一个小困境。我有一个函数,它使用自己的连接字符串(可以相同或不同)浏览数据集中的查询列表,并开始填充所有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(我不能改变它)。目前这不起作用,不确定我做错了什么。
答案 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; }
}
}