[英]Possible to process non-thread-safe code in parallel?
我有一个在DataRows
上执行foreach
的C#程序。 简化版为:
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;
}
尝试对同一SQL Server的多个Fill
调用进行多线程将是一场艰苦的战斗。 SQL Server和网络都已经尽可能快地向您发送数据。 通过将Fill
调用划分为多个线程,您实际上会减慢速度。 带宽将在每个线程之间平均分配(因此不会提高性能),除了每个线程也会增加一些开销,这会降低速度。 您添加的每个线程都会使这个问题更加复杂。
如果每个Fill
在不同的数据库上运行,那么您可能会在线程方面有所改进,因为尽管网络争用仍然是一个问题,但它们不会竞争相同的SQL资源。
我在您的代码中看不到任何会使多线程不安全的东西,只是看不到对其进行线程处理将如何改善您的处理时间。
注释表明您实际上是根据数据生成报告,并且这就是您要进行多线程处理的内容。 这可能实际上可行。 只要报告仅读取数据,而不写入数据,您就应该能够对它们进行线程化,并共享单个数据集。
根据MSDN的说明 , DataTables
和DataSet
具有此注释“此类型对于多线程读取操作是安全的。您必须同步任何写入操作。”
它应该是完全正常的共享同一个DataSet
只要线程之间例如因为你只从它读 , 写都需要进行特殊处理。
这段代码似乎有效:
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);
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.