C# 调用相同方法的并发线程会导致ConcurrentBag中出现重复的线程<;数据行>;收集
我必须在一个线程上处理一个大数据集,这需要一个多小时。我已经实现了一些多线程来加速这个过程。每个线程都处理特定范围的数据,没有重叠,但当它们将结果插入到我创建的C# 调用相同方法的并发线程会导致ConcurrentBag中出现重复的线程<;数据行>;收集,c#,multithreading,concurrency,duplicates,C#,Multithreading,Concurrency,Duplicates,我必须在一个线程上处理一个大数据集,这需要一个多小时。我已经实现了一些多线程来加速这个过程。每个线程都处理特定范围的数据,没有重叠,但当它们将结果插入到我创建的ConcurrentBag集合中时,会出现一些重复 这怎么可能?任何关于我可以做得更好的建议都将不胜感激 主要方法: public static ConcurrentBag<DataRow> finalRowList = new ConcurrentBag<DataRow>(); //Create a concur
ConcurrentBag
集合中时,会出现一些重复
这怎么可能?任何关于我可以做得更好的建议都将不胜感激
主要方法:
public static ConcurrentBag<DataRow> finalRowList = new ConcurrentBag<DataRow>(); //Create a concurrent collection of datarows so we can thread these calculations
public static DataTable results = new DataTable(); //Final datatable the datarows are added to
static void Main(string[] args)
{
//The goal is to calculate correlation between each item in list 1 against each item in list 2
List<string> Variable1List = populateVariable1List(); //Primary List of distinct items to iterate over
List<string> Variable2List = populateVariable2List(); //Secondary list of distinct items
DateTime endDate = new DateTime(2020, 3, 31);
//Separate threads based on alphabetic ranges so there is no overlap
Thread t1 = new Thread(() => CalculatePairCorrelation(Variable1List.Where(s => string.Compare(s, "G") < 0), Variable2List, endDate));
Thread t2 = new Thread(() => CalculatePairCorrelation(Variable1List.Where(s => string.Compare(s, "G") >= 0 && string.Compare(s, "M") < 0), Variable2List, endDate));
Thread t3 = new Thread(() => CalculatePairCorrelation(Variable1List.Where(s => string.Compare(s, "M") >= 0 && string.Compare(s, "S") < 0), Variable2List, endDate));
Thread t4 = new Thread(() => CalculatePairCorrelation(Variable1List.Where(s => string.Compare(s, "S") >= 0), Variable2List, endDate));
List<Thread> threads = new List<Thread>();
threads.Add(t1);
threads.Add(t2);
threads.Add(t3);
threads.Add(t4);
foreach (Thread t in threads)
{
t.Start();
}
foreach (Thread t in threads)
{
t.Join();
}
//Add rows from finalRowList to final datatable
foreach (var dr in finalRowList)
{
results.Rows.Add(dr);
}
}
public static ConcurrentBag finalRowList=new ConcurrentBag()//创建数据行的并发集合,以便我们可以执行这些计算
公共静态数据表结果=新数据表()//将数据行添加到的最终数据表
静态void Main(字符串[]参数)
{
//目标是计算列表1中每个项目与列表2中每个项目之间的相关性
List Variable1List=populateVariable1List();//要迭代的不同项的主列表
List Variable2List=populateVariable2List();//不同项的二级列表
DateTime endDate=新的日期时间(2020,3,31);
//根据字母范围分开线程,因此没有重叠
线程t1=新线程(()=>CalculatePairCorrelation(Variable1List.Where(s=>string.Compare(s,“G”)<0),Variable2List,endDate));
线程t2=新线程(()=>CalculateAirCorrelation(Variable1List.Where(s=>string.Compare(s,“G”)>=0&&string.Compare(s,“M”)<0),Variable2List,endDate));
线程t3=新线程(()=>CalculateAirCorrelation(Variable1List.Where(s=>string.Compare(s,“M”)>=0&&string.Compare(s,“s”)<0),Variable2List,endDate));
线程t4=新线程(()=>CalculatePairCorrelation(Variable1List.Where(s=>string.Compare(s,“s”)>=0),Variable2List,endDate));
列表线程=新列表();
添加(t1);
添加(t2);
添加(t3);
添加(t4);
foreach(螺纹中的螺纹t)
{
t、 Start();
}
foreach(螺纹中的螺纹t)
{
t、 Join();
}
//将finalRowList中的行添加到最终数据表
foreach(最终列表中的var dr)
{
结果.行.添加(dr);
}
}
CalculatePairCorrelation()代码:
publicstaticvoidcalculateAirCorrelation(IEnumerablest1、IEnumerablest2、DateTime endDate、int行)
{
foreach(列表1中的变量项1)
{
foreach(列表2中的变量项2)
{
double r10=计算相关性(第1项、第2项、结束日期、第10项);
双r30=计算相关性(第1项、第2项、结束日期,30);
var dr=results.NewRow();
dr[“Item1”]=Item1;
dr[“项目2”]=项目2;
dr[“R10”]=R10;
dr[“R30”]=R30;
finalRowList.Add(dr);//添加到线程安全集合
}
}
}
问题可能与这一行有关,这一行在并行路径中被称为:
var dr = results.NewRow();
创建DataRow
可能会变异底层DataTable
,这不是线程安全类
我的建议是远离并发收集和数据的手动分区,而是使用易于使用且更难出错的方法:
var resultsList = Variable1List
.SelectMany(_ => Variable2List, (Item1, Item2) => (Item1, Item2))
.AsParallel()
.AsOrdered() // Optional
.WithDegreeOfParallelism(4) // Optional
.Select(pair => (
Item1: pair.Item1,
Item2: pair.Item2,
R10: CalculateCorrelation(pair.Item1, pair.Item2, endDate, 10),
R30: CalculateCorrelation(pair.Item1, pair.Item2, endDate, 30)
))
.ToList();
foreach (var result in resultsList)
{
var dr = results.NewRow();
dr["Item1"] = result.Item1;
dr["Item2"] = result.Item2;
dr["R10"] = result.R10;
dr["R30"] = result.R30;
results.Rows.Add(dr);
}
若你们有DUP,首先要寻找的是一种分裂机制。您应该使用队列,您的问题将得到解决。您可以再详细说明一下吗?您可以创建膨胀收集队列,并在一个线程中执行
任务。运行(()=>{加载您的队列})
。然后创建1-n个使用者线程任务。运行(()=>{get from queue and process})
。等待结束任务。等待(生产者、消费者1、消费者2等)
这保证了您快速可靠的多线程处理我为Item1列表实现了一个全局ConcurrentQueue
,并为每个线程实现了Item2列表的一个新实例。我仍然有同样数量的副本。我需要进一步挖掘…我认为创建datarow只是创建一个与表具有相同模式的新行。我不知道它实际上会改变桌子。我会给你一个机会,然后再报告。并行的唯一问题是,你没有太多的控制权。你永远不知道有多少线程在运行。@eek你是对的。创建新行不会将此行添加到表中,这是DataTable.NewRow
方法的源代码。通过快速查看,它似乎确实改变了类的内部状态。您正确地认为NewRow()调用不是线程安全的!我更改了代码以生成一个空行的并发集合,然后在填充每一行时执行TryTake()。谢谢
var resultsList = Variable1List
.SelectMany(_ => Variable2List, (Item1, Item2) => (Item1, Item2))
.AsParallel()
.AsOrdered() // Optional
.WithDegreeOfParallelism(4) // Optional
.Select(pair => (
Item1: pair.Item1,
Item2: pair.Item2,
R10: CalculateCorrelation(pair.Item1, pair.Item2, endDate, 10),
R30: CalculateCorrelation(pair.Item1, pair.Item2, endDate, 30)
))
.ToList();
foreach (var result in resultsList)
{
var dr = results.NewRow();
dr["Item1"] = result.Item1;
dr["Item2"] = result.Item2;
dr["R10"] = result.R10;
dr["R30"] = result.R30;
results.Rows.Add(dr);
}