C# 这是C“Parallel.ForEach”吗;“最后”;方法线程安全?
由于MicroSoft所述的优点,我正在使用C# 这是C“Parallel.ForEach”吗;“最后”;方法线程安全?,c#,thread-safety,parallel.foreach,C#,Thread Safety,Parallel.foreach,由于MicroSoft所述的优点,我正在使用Parallel.ForEach(顺便说一句:我现在找不到它,但我很确定我在某个地方读到过,Parallel.ForEach在所有迭代执行之前不会返回控制。这是真的吗?) 我从一开始。我不想使用已接受的答案,因为它放弃了Parallel.ForEach,而只使用了Task.Run(或.Net 4.0的Task Factory.StartNew)。同时,O.P.使用“锁定”将更新同步到DataTable列表中,据我所知,这可能会降低Parallel.Fo
Parallel.ForEach
(顺便说一句:我现在找不到它,但我很确定我在某个地方读到过,Parallel.ForEach在所有迭代执行之前不会返回控制。这是真的吗?)
我从一开始。我不想使用已接受的答案,因为它放弃了Parallel.ForEach
,而只使用了Task.Run
(或.Net 4.0的Task Factory.StartNew
)。同时,O.P.使用“锁定”将更新同步到DataTable
列表中,据我所知,这可能会降低Parallel.ForEach
的效率
因此,使用本文,我编写了下面的strawman代码。目标是将threadLocalDataTable
(每个select语句一个)累积到dataTableList
,所有查询完成后返回该列表。它可以工作,但我想知道localFinally
方法是否真的是线程安全的(请参阅带有注释//null的代码行,(s,loop,threadLocalDataTable)=>
{
DataTable=null;
使用(SqlCommand SqlCommand=new SqlCommand())
{
使用(SqlConnection SqlConnection=newsqlconnection(s.ConnectionString))
{
sqlConnection.Open();
sqlCommand.CommandType=CommandType.Text;
sqlCommand.Connection=sqlConnection;
sqlCommand.CommandText=s.SelectQuery;
SqlDataAdapter SqlDataAdapter=新的SqlDataAdapter(sqlCommand);
dataTable=新的dataTable
{
TableName=s.DataTableName
};
dataTable.Clear();
dataTable.Rows.Clear();
dataTable.Columns.Clear();
sqlDataAdapter.Fill(dataTable);
Dispose();
sqlConnection.Close();
}
}
返回数据表;
},(threadLocalDataTable)=>dataTableList.Add(threadLocalDataTable)//您提供的链接所提供的文档清楚地说明了(关于localFinally):
此委托可以由多个任务同时调用
所以不,它不是线程安全的。但这不是唯一的问题——您的整个实现都是错误的
传递给每个循环迭代的“Thread local”变量是累加器。它累加在同一线程上运行的多个迭代所获得的结果。您的实现完全忽略该变量并始终返回一个数据表。这意味着如果您的表多于并行循环的线程数(默认为处理器的内核数)-实际上您正在失去结果,因为您忽略了累加器
正确的方法是使用List
作为累加器,而不是单个累加器。例如:
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(
sqlList,
()=>新建列表(),//初始化累加器
(s、循环、threadLocalDataTables)=>
{
数据表结果=。。。;
//另外,这是线程安全的
threadLocalDataTables.Add(结果);
//将累加器返回到下一个迭代
返回threadLocalDataTables;
},(threadLocalDataTables)=>
{
//客户端中的并行任务不会影响SQL server。请查看SQL profiler。没有性能提升。我为async/await编写了strawman代码,但出于各种原因将其丢弃。请查看此问题:
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);
});