由于MicroSoft的好处,我正在使用Parallel.ForEach
实现大量的SQL SELECT语句(其中有成千上万个)(顺便说一句:我现在找不到它,但是我很确定我在某处读过在执行所有迭代之前,Parallel.ForEach不会返回控制。这是真的吗?)
我从Parallel.Foreach SQL querying sometimes results in Connection开始。我不想使用已接受的答案,因为它放弃了Parallel.ForEach
,而只使用了Task.Run
(或.net 4.0中的Task Factory.StartNew
)。同时,O.P。使用“锁定”将更新同步到DataTable
列表,据我了解,这可能会降低Parallel.ForEach
的效率。
因此,使用文章How to: Write a Parallel.ForEach Loop with Thread-Local Variables,我在下面编写了草编代码。目标是将threadLocalDataTable
(每个select语句一个)累积到所有查询完成后返回的dataTableList
。它可以工作,但是我想知道localFinally
方法是否真的是线程安全的(请参见带有注释//<-- the localFinally method in question
的代码行。注意:SqlOperation类实现了连接字符串,输出数据表名称和选择查询字符串
public static IList<DataTable> GetAllData(IEnumerable<SqlOperation> sqlList)
{
IList<DataTable> dataTableList = new List<DataTable>();
dataTableList.Clear();
try
{
Parallel.ForEach<SqlOperation, DataTable>(sqlList, () => null, (s, loop, threadLocalDataTable) =>
{
DataTable dataTable = null;
using (SqlCommand sqlCommand = new SqlCommand())
{
using (SqlConnection sqlConnection = new SqlConnection(s.ConnectionString))
{
sqlConnection.Open();
sqlCommand.CommandType = CommandType.Text;
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandText = s.SelectQuery;
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand);
dataTable = new DataTable
{
TableName = s.DataTableName
};
dataTable.Clear();
dataTable.Rows.Clear();
dataTable.Columns.Clear();
sqlDataAdapter.Fill(dataTable);
sqlDataAdapter.Dispose();
sqlConnection.Close();
}
}
return dataTable;
}, (threadLocalDataTable) => dataTableList.Add(threadLocalDataTable) //<-- the localFinally method in question
);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString(), "GetAllData error");
}
return dataTableList;
}
最佳答案
您所提供的链接中的文档明确指出了(关于localFinally):
该委托可以被多个任务同时调用
因此,它不是线程安全的。但这不是唯一的问题-您的整个实现都不正确。
传递给每个循环迭代的“线程局部”变量是累加器。它累积在同一线程上运行的多次迭代获得的结果。您的实现完全忽略该变量,并且始终返回一个数据表。这意味着,如果您的表多于并行循环的线程数(默认为处理器的内核数),那么实际上是在浪费结果,因为您忽略了累加器。
正确的方法是使用List<DataTable>
作为累加器,而不是单个累加器。例如:
Parallel.ForEach<SqlOperation, List<DataTable>>(
sqlList,
() => new List<DataTable>(), // initialize accumulator
(s, loop, threadLocalDataTables) =>
{
DataTable result = ...;
// add, that's thread safe
threadLocalDataTables.Add(result);
// return accumulator to next iteration
return threadLocalDataTables;
}, (threadLocalDataTables) =>
{
//<-- the localFinally method in question
lock (dataTableList) // lock and merge results
dataTableList.AddRange(threadLocalDataTables);
});
就是说-使用
Parallel.ForEach
来加快IO绑定工作(例如进行SQL查询)不是一个好主意,使用async \ await会更好。但这超出了这个问题的范围。关于c# - 这个C#Parallel.ForEach“localFinally”方法线程安全吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49327203/