1

私はWinFormsアプリケーションを書いています。データベースからデータを取得し、そのデータ セットに対していくつかのアクションを実行してから、データベースに保存する予定です。データベース内の 1 つのテーブルのみに関心があるため、LINQ to SQL を使用してデータベースへのクエリを実行しているため、このために ORM 全体を実装したくありませんでした。

DBからデータセットを取得しています。ただし、データセットはかなり大きいです。したがって、現在私がやろうとしているのは、データセットを 4 つの比較的等しいサイズのリスト ( List<object>) に分割することです。

次に、これらのリストのそれぞれを実行し、アクションを実行し、その間にその進行状況を報告する別のバックグラウンド ワーカーを用意します。4 つのバックグラウンド ワーカーすべてがセクションの処理を完了したら、これらのセクションを 1 つの大きなリストに統合する予定です。

しかし、バックグラウンド ワーカーが一意のリストを処理している間、エラーが発生し続けます。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;
    }

この同じ関数が 4 つのワーカーすべてによって呼び出され、関数と呼ばれるワーカーに基づいて 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 を共有しているときに同じエラーが発生しました。このシナリオでは、次の 2 つのオプションしかありません。

1) スレッドを開始する前.ToList()に、クエリを呼び出して、 ではなく実際の を作成listFromDBIQueryable<>ますList<>。これは、クエリが既に実行されており、スレッドが DataContext ではなく実際の List で動作していることを意味します。

2) DataContext 定義を各スレッドに移動します。DataContext が共有されなくなったため、エラーはなくなりました。

3 番目のオプションは、シナリオを別のものに書き直すことです (たとえば、単一のバックグラウンド スレッドですべてをシーケンシャルにするなど)...

于 2016-02-27T18:51:54.137 に答える
0

まず第一に、複数のワーカー スレッドが必要な理由がまったくわかりません。(これらのリストは別々のデータベース/テーブル/サーバーにありますか?4つのリストがある場合に4つの進行状況バーを表示したいですか、それともこれらの進行状況レポートを1つの奇妙な進行状況バーにマージしていますか:D

また、データベースの更新処理を高速化しようとしていますが、linq を sql に SAVES を送信しないため、実際にはトランザクションをバッチ処理していません。最後にすべてを 1 つの大きなトランザクションに保存するだけです。それは本当にあなたが目指しているものですか?プログレス バーは 100% で停止し、SQL 側で多くの時間を費やします。

1 つのバックグラウンド スレッドを作成し、すべてを同期的に処理しますが、数行ごとに保存トランザクションをバッチ処理します (1000 行ごとなどをお勧めしますが、これを試してください)。数百万行であっても高速です。

このマルチスレッド ソリューションが本当に必要な場合: 「同じキーを持つ別の blabla が追加されました」というエラーは、同じアイテムを複数の「mylists」に追加するか、同じアイテムを同じリストに 2 回追加することを示唆しています。エラーはありますか?

于 2013-06-12T19:02:35.017 に答える