Avatar billede webit Nybegynder
06. juli 2011 - 14:40 Der er 10 kommentarer og
1 løsning

Problem med Paralle.ForEach

Hej Eksperter,

Jeg har to funktionen som kan ses her under som hentet noget data ud af en tabel og derefter gen-indsættet det i en anden tabelle.

public IEnumerable<string> ReadData()
    {
        SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["dbconnect"].ConnectionString);

        SqlCommand command = new SqlCommand("GetAllData", myConnection);
        command.CommandType = CommandType.StoredProcedure;



        command.Connection.Open();

        SqlDataReader userreader = command.ExecuteReader(CommandBehavior.CloseConnection);


        if (userreader.HasRows)
        {
            while (userreader.Read()) {
                yield return userreader["Content"].ToString();

            }
        }
    }

    public void CheckBlogAccess()
    {
        Parallel.ForEach(this.ReadData(), Reader =>
        {
            Backup(Reader);
        });
    }

og funktionen køre også fint men ca 33 % at de ny oprettet poster i den nye tabel er tomme ? det virker som om at Backup()ikke får indholdet af "Reader" med er der nogle der ved hvordan det kan være ?
Avatar billede Michael Johansen Nybegynder
06. juli 2011 - 15:08 #1
Det er nok en god idé at bruger Parallel LINQ i stedet.
Prøv at se dette eksempel:
http://msdn.microsoft.com/en-us/library/dd460714.aspx
Avatar billede aaberg Nybegynder
06. juli 2011 - 15:20 #2
Det du har gang i kan ikke lade sig gøre, og jeg tvivler stærkt på at du får bedre performance ud af det.

Ikke prøv at læse en SqlDataReader fra flere tråde på en gang. Problemet er, at du har ingen kontrol over, hvornår den kalder userreader.Read(), og hvornår den returnere readerens værdi. I mellemtiden kan det godt være at en af de andre tråde har kaldt userreader.Read(), og så mister du rows!

Du får heller ikke noget ud af at gøre det. Databasen bliver ikke på magisk vis hurtigere af at du tilgår den med x-antal tråde samtiddig. Tvært imod, risikere du at tingene bliver BETYDELIG langsommere, pga context switching. Du får best performance fra en reader, ved kun at læse fra en tråd.

Hvis du bare skal læse fra en tabel/database og skrive til en anden tabel/database, anbefaler jeg at se på SqlBulkCopy klassen:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx
At bruge SqlBulkCopy klassen til dette, giver maksimum performance.
Avatar billede webit Nybegynder
06. juli 2011 - 15:46 #3
Tak for svaret!! fik det til at virke!
Avatar billede Syska Mester
06. juli 2011 - 16:04 #4
Hvis det er premature optimazation ... så ville jeg lave det single threaded til at startet med, og derefter se på muligheder for at lave det bedre.

mvh
Avatar billede arne_v Ekspert
07. juli 2011 - 04:04 #5
En saadan iterator er ikke thread safe, saa koden i spoergsmaalet er ikke thread safe.

http://msmvps.com/blogs/jon_skeet/archive/2009/10/23/iterating-atomically.aspx

har nogle forslag til at lave en iterator thread safe.

Men medmindre der er tale om hundredevis af millioner af raekker, saa var det nemmeste nok at materialisere til en List<>.

Database operationen kan ikke paralleliseres saa det maa jo vaere den Backup funktion der skal paralleliseres og det virker jo fint med en List<>.
Avatar billede janus_007 Nybegynder
07. juli 2011 - 10:10 #6
"Du får heller ikke noget ud af at gøre det. Databasen bliver ikke på magisk vis hurtigere af at du tilgår den med x-antal tråde samtiddig. Tvært imod, risikere du at tingene bliver BETYDELIG langsommere, pga context switching. Du får best performance fra en reader, ved kun at læse fra en tråd."

Her er jeg ikke helt enig :)
- Det er klart nok at SqlDataReaderen vil opføre sig besynderligt, den vil faktisk fejle med noget alá "There is already an open DataReader associated"

- Men at oprette flere connections og skrive til databasen er absolut effektivt. Databasen skal nok holde styr på antallet og connections og tage insert/ selects i den optimale rækkefølge. Så kan vi diskutere dataens natur og igen hvis det blot er enslydende rækker som skal indsættes burde man overveje en bulkløsning.
Avatar billede arne_v Ekspert
07. juli 2011 - 15:06 #7
Da der kun er en data reader, saa vil man naeppe faa fejlen "There is already an open DataReader associated".
Avatar billede janus_007 Nybegynder
07. juli 2011 - 16:28 #8
Nåh Nåh... nu ikke så smart Arne :-P

Jeg ser nu at en ny connection og reader bliver oprettet, derfor må fejlen ligge i din Backup.

Må vi se den kode?
Avatar billede janus_007 Nybegynder
07. juli 2011 - 16:30 #9
Men hvordan vil du styre GetAllData, den henter du jo og mange gange!, hvis du vil gøre sådan bliver du nødt til at sætte en range på af en art :)
Avatar billede arne_v Ekspert
08. juli 2011 - 03:09 #10
Der bliver oprettet en connection og en data reader og SP bliver kaldt en gang.
Avatar billede arne_v Ekspert
08. juli 2011 - 03:16 #11
For at forstaa hvad der sker saa proev:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace E
{
    public class Program
    {
        public static IEnumerable<int> Test()
        {
            Console.WriteLine("Vi opretter connection");
            Console.WriteLine("Vi kalder SP");
            Console.WriteLine("Vi åbner data reader");
            for(int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Vi læser række");
                yield return i;
            }
            Console.WriteLine("Her glemmer vi at lukke data reader og connection");
        }
        public static void Main(string[] args)
        {
            Parallel.ForEach(Test(), v => Console.WriteLine(v));
            Console.ReadKey();
        }
    }
}
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester