1

我正在编写一个 WinForms 应用程序。我正在从我的数据库中提取数据,对该数据集执行一些操作,然后计划将其保存回数据库。我正在使用 LINQ to SQL 对数据库执行查询,因为我只关心数据库中的 1 个表,所以我不想为此实现整个 ORM。

我让它从数据库中提取数据集。但是,数据集相当大。所以目前我正在尝试做的是将数据集分成 4 个相对相等大小的列表(List<object>)。

然后我有一个单独的后台工作人员来运行每个列表,执行操作并在执行此操作时报告其进度。一旦所有 4 名后台工作人员都完成了他们的部分的处理,我计划将这些部分合并到一个大列表中。

但是当后台工作人员正在处理他们的唯一列表时,我不断收到错误消息。即使对象已转换为 List 对象,它们是否仍保持与 LINQ to SQL 的 DataContext 的联系?任何想法如何解决这一问题?我对多线程的经验很少,所以如果我完全错了,请告诉我。

多谢你们。如果您需要任何代码片段或任何其他信息,请询问。

编辑:哎呀。我完全忘了给出错误信息。在 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 提供了不同的列表。

4

3 回答 3

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 不再共享,不再出现错误。

第三种选择是将场景重新写入其他内容,就像您所做的那样(例如,使所有内容在单个后台线程上按顺序排列)......

于 2016-02-27T18:51:54.137 回答
0

首先,我真的不明白为什么你需要多个工作线程。(这些列表是否在单独的数据库/表/服务器中?如果您有 4 个列表,您是否真的想显示 4 个进度条,或者您是否以某种方式将这些进度报告合并到一个奇怪的进度条中:D

此外,您正在尝试加快处理数据库的更新,但您没有将 linq 发送到 sql 任何 SAVES,因此您并没有真正批量处理事务,您只需在一个大事务中保存所有内容,这真的是你的目标吗?进度条将在 100% 处停止,然后在 SQL 端花费大量时间。

只需创建一个后台线程并同步处理所有内容,但每隔几行批处理一次保存事务(我建议每 1000 行一次,但你应该尝试一下),它会很快,即使有数百万行,

如果你真的需要这个多线程解决方案:“another blabla with the same key has been added”错误提示你将同一个项目添加到多个“mylists”,或者将同一个项目添加到同一个列表两次,否则怎么会有有任何错误吗?

于 2013-06-12T19:02:35.017 回答
0

使用并行 LINQ (PLINQ),您可以利用多个 CPU 内核来处理您的数据。但是,如果您的应用程序要在单核 CPU 上运行,那么将数据拆分为和平不会给您带来性能优势,而是会产生一些上下文更改开销。

希望能帮助到你

于 2013-06-12T19:11:23.950 回答