使用LINQ to SQL进行多线程处理

时间:2013-06-12 17:11:05

标签: c# .net multithreading linq-to-sql

我正在编写一个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指定一个不同的列表。

3 个答案:

答案 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上运行,那么将数据拆分为和平并不能为您带来性能优势,而是会产生一些上下文变化的开销。

希望它有助于