我正在编写一个WinForms应用程序。我从数据库中提取数据,对该数据集执行某些操作,然后计划将其保存回数据库。我正在使用LINQ to SQL来执行对数据库的查询,因为我只关心数据库中的1个表,所以我不想为此实现整个ORM。
我从数据库中提取数据集。但是,数据集相当大。所以我目前要做的是将数据集分成4个相对大小相同的列表(List<object>
)。
然后我有一个单独的后台工作程序来遍历每个列表,执行操作并报告其进度。我计划在所有4名后台工作人员完成他们的部分处理后,将这些部分合并为一个大的列表。
但是当后台工作人员正在处理他们的唯一列表时,我一直收到错误。对象是否与LINQ to SQL的DataContext保持联系,即使它们已经转换为List对象?任何想法如何解决这一问题?我对多线程的经验很少,所以如果我完全错了,请告诉我。
谢谢你们。如果您需要任何代码段或任何其他信息,请询问。
编辑糟糕。我完全忘了给出错误信息。在DataContext designer.cs中,它在An item with the same key has already been added.
函数上提供了错误SendPropertyChanging
。
private void Setup(){
List<MyObject> quarter1 = _listFromDB.Take(5000).ToList();
bgw1.RunWorkerAsync();
}
private void bgw1_DoWork(object sender, DoWorkEventArgs e){
e.Result = functionToExecute(bgw1, quarter1);
}
private List<MyObject> functionToExecute(BackgroundWorker caller, List<MyObject> myList)
{
int progress = 0;
foreach (MyObject obj in myList)
{
string newString1 = createString();
obj.strText = newString;
//report progress here
caller.ReportProgress(progress++);
}
return myList;
}
所有四个工作人员都会调用同一个函数,并根据哪个工作者称为函数为myList指定一个不同的列表。
答案 0 :(得分:2)
因为还没有发布真正的答案,我会试一试。 鉴于您还没有显示任何LINQ-to-SQL代码(不使用DataContext) - 我会采取有根据的猜测,即DataContext在线程之间共享,例如:
using (MyDataContext context = new MyDataContext())
{
// this is just some random query, that has not been listed - ToList()
// thus query execution is defered. listFromDB = IQueryable<>
var listFromDB = context.SomeTable.Where(st => st.Something == true);
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
var list1 = listFromDB.Take(5000).ToList(); // runs the SQL query
// call some function on list1
});
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
var list2 = listFromDB.Take(5000).ToList(); // runs the SQL query
// call some function on list2
});
}
现在你得到的错误 - An item with the same key has already been added.
- 是因为DataContext对象不是线程安全的!很多东西都在后台发生 - DataContext必须从SQL加载对象,跟踪它们的状态等。这个后台工作抛出了错误(因为每个线程都在运行查询,DataContext被访问)。
至少这是我个人的经历。在多个线程之间共享DataContext时遇到相同的错误。在这种情况下,您只有两个选项:
1)在开始线程之前,请在查询上调用.ToList()
,使listFromDB
不是IQueryable<>
,而是实际List<>
。这意味着查询已经运行,并且线程在实际List上运行,而不是在DataContext上运行。
2)将DataContext定义移动到每个线程中。因为不再共享DataContext,所以不再有错误。
第三个选项是将场景重新编写为其他内容,就像你所做的那样(例如,在一个后台线程中使所有内容顺序完成)......
答案 1 :(得分:0)
首先,我真的不明白为什么你需要多个工作线程。 (这些是单独的数据库/表/服务器中的列表吗?如果您有4个列表,或者您是否以某种方式将这些进度报告合并到一个奇怪的进度条中,您真的想要显示4个进度条:D
另外,您正在尝试加快对数据库的处理更新,但是您没有将linq发送到sql任何SAVES,因此您不是真正批处理事务,您只需将所有内容保存在一个大交易,这真的是你的目标吗?进度条将停止在100%,然后在SQL端花费大量时间。
只需创建一个后台线程并同步处理所有内容,但每隔几行批量一次保存事务(我建议每1000行一次,但你应该试试这个),它会很快,即使数百万行,
如果您确实需要这种多线程解决方案: “添加了相同密钥的另一个blabla”错误表明您将相同的项目添加到多个“mylists”,或者将相同的项目添加到同一个列表两次,否则将如何出现任何错误?
答案 2 :(得分:0)
使用Parallel LINQ (PLINQ),您可以利用多个CPU内核来处理数据。但是,如果您的应用程序将在单核CPU上运行,那么将数据拆分为和平并不能为您带来性能优势,而是会产生一些上下文变化的开销。
希望它有助于