Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/268.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 这是C“Parallel.ForEach”吗;“最后”;方法线程安全?_C#_Thread Safety_Parallel.foreach - Fatal编程技术网

C# 这是C“Parallel.ForEach”吗;“最后”;方法线程安全?

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

由于MicroSoft所述的优点,我正在使用
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);
    });