我有一个C#程序在foreach
上执行DataRows
。简化版本是:
foreach (DataRow row in masterReportData.Rows)
{
LoopThruReports(row, p2, p3, p4);
}
LoopThruReports
被声明为:
public static void LoopThruReports(DataRow row, DataTable p2, string p3, string p4)
我真的希望让迭代并行运行,因为它们从数据库中填充SqlDataAdapter
(需要一段时间)。尽管尝试了线程,任务和并行库,但我无法弄清楚这是否可行。如果只有我可以让每个线程不与其他线程共享变量!有没有人对这是否可能有任何想法,如果是的话,我怎么能这样做?感谢。
编辑:LoopThruReports
方法调用GetGCHAReportDetailData
,这是Fill
完成的地方。这是这个方法的样子:
public static DataSet GetGCHAReportDetailData(int reinsuranceContract, int billingMode, DateTime reportPeriod)
{
DataSet dsResults = new DataSet();
DataTable gchaReportData = new DataTable();
string connStr = ConfigurationManager.ConnectionStrings["UtopiaConnString"].ConnectionString;
using (SqlCommand sqlCmd = new SqlCommand("dbo.[sproc_GCHADetailDataForContract]"))
{
sqlCmd.CommandType = CommandType.StoredProcedure;
//set the command time out to 60 minutes b/c this report takes time to generate
sqlCmd.CommandTimeout = 0;
SqlParameter parm = new SqlParameter("@ReinsuranceContractId", SqlDbType.Int);
parm.Value = reinsuranceContract;
sqlCmd.Parameters.Add(parm);
SqlParameter parm1 = new SqlParameter("@BillingMode", SqlDbType.Int);
parm1.Value = billingMode;
sqlCmd.Parameters.Add(parm1);
SqlParameter parm2 = new SqlParameter("@ReportingPeriod", SqlDbType.DateTime);
parm2.Value = reportPeriod;
sqlCmd.Parameters.Add(parm2);
sqlCmd.CommandTimeout = 0;
using (SqlConnection conn = new SqlConnection(connStr))
{
sqlCmd.Connection = conn;
using (SqlDataAdapter da = new SqlDataAdapter(sqlCmd))
{
try
{
conn.Open();
da.Fill(dsResults);
conn.Close();
}
catch (Exception ex)
{
logger.Error(ex, "Error retrieving data from the database.\r\n Message: {0}", ex.Message);
throw;
}
finally
{
if (conn.State != ConnectionState.Closed)
conn.Close();
}
}
}
}
return dsResults;
}
答案 0 :(得分:1)
尝试多线程多个Fill
调用同一个SQL服务器将是一场失败的战斗。 SQL服务器和网络都已经尽可能快地向您发送数据。通过在多个线程上分割Fill
次调用,实际上会减慢速度。带宽将在每个线程之间平均分配(因此不会增加性能),除了每个线程也会产生一些开销,这会使事情变慢一些。您添加的每个线程都会更加复杂化这个问题。
如果每个Fill
针对不同的数据库运行,那么可能能够获得一些线程改进,因为他们不会竞争相同的SQL资源,尽管网络争论仍然是一个问题。
我在代码中看不到任何会导致多线程不安全的内容,我只是不知道如何通过线程来改善处理时间。
注释表明您实际上是从数据生成报告,这就是您想要多线程的。这实际上可行。只要报表只读取数据,从不写数据,您就应该能够对它们进行线程化,并共享单个数据集。
根据MSDN,DataTables
和DataSet
有此注释"此类型对于多线程读取操作是安全的。您必须同步任何写操作。"
在线程之间共享相同的DataSet
实例应该完全没问题,只要您读取,编写就需要特殊处理。< / p>
答案 1 :(得分:0)
此代码似乎有效:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ParallelDBProcessing
{
class Program
{
static void Main(string[] args)
{
List<Task> taskList = new List<Task>();
for (int i = 1; i <= 4; i++)
{
int temp = i;
Task task = Task.Run(() => DoWork(temp)); // use Task.Run if you want to get task.Result back
taskList.Add(task);
}
Task.WaitAll(taskList.ToArray());
}
public static void DoWork(int num)
{
Console.WriteLine(num);
string connstr = @"Data Source = (local)\sqlexpress; Initial Catalog = Barry; Integrated Security = true";
DataSet dsResults = new DataSet();
using (SqlCommand sqlCmd = new SqlCommand("dbo.[spMagicProc]"))
{
sqlCmd.CommandType = CommandType.StoredProcedure;
SqlParameter parm = new SqlParameter("@Input_A", SqlDbType.Int);
parm.Value = num;
sqlCmd.Parameters.Add(parm);
SqlParameter parm1 = new SqlParameter("@Input_B", SqlDbType.Int);
parm1.Value = 2;
sqlCmd.Parameters.Add(parm1);
using (SqlConnection conn = new SqlConnection(connstr))
{
sqlCmd.Connection = conn;
using (SqlDataAdapter da = new SqlDataAdapter(sqlCmd))
{
try
{
conn.Open();
da.Fill(dsResults);
conn.Close();
}
catch (Exception ex)
{
throw;
}
finally
{
if (conn.State != ConnectionState.Closed)
conn.Close();
}
}
}
Console.WriteLine(dsResults.Tables[0].Rows.Count);
}
}
}
}