C# watki - wczytywanie dużych plików, zapisywanie z wątków

0

Witam!
Od 20h poznaje C# ;D więc proszę po poradę:

Aplikacja (webcrawler) na wejściu dostaje duży plik TXT z URLami (30MB to norma) i po kolei:
pobiera źródło URLa
szuka ciągów Regexp
zapisuje znalezione ciągi do pliku

Jak to teraz wykonać wielowątkowo bo mam wątpliwości z:

  • plik wejściowy jest duży wiec nie mogę go załadować całego :) muszę czytać linijka po linijce by nie otwierać go co wątek
  • wątków nie mogę odpalić tyle ile urli :)
  • jak odpalę 20 wątków to jak wykryć ile ich zostało i jak uruchamiać kolejne by zawsze procowało ich np 50?
  • co z zapisywaniem wyników przez wątki? od kiedy mam ssd to nie widzi mi się otwieranie i zapisywanie jednej linijki po 20 tys razy :-)

Przekopałem google i nigdzie nie widzę przykładów które kontrolują ilość wątków. Co prawda są jakieś kolejki ale przecież nie załaduje 1mln wątków do oczekiwania na uruchomienie. Parallel foreach udało mi się uruchomić i mi pasuje idealnie (poza pytaniami o tryb odczytu,zapisu na dysku) gdyby nie ograniczenie do 4 wątków (tyle mam rdzeni).

Proszę o pomoc jak to wykonać najprościej, urle/przykłady/filmiki bardzo będą pomocne.

1

30MB to znowu nie tak dużo, przy dzisiejszych ilościach pamięci RAM to nie jest problemem.
Zainteresuj się pulami wątków ThreadPool i kolejka będzie tu dobrym rozwiązaniem, plus kolejka do której będziesz pakował pobrane dane i osobny jeden wątek, który będzie pisał do pliku.

0
LordNET napisał(a):

Przekopałem google i nigdzie nie widzę przykładów które kontrolują ilość wątków. Co prawda są jakieś kolejki ale przecież nie załaduje 1mln wątków do oczekiwania na uruchomienie. Parallel foreach udało mi się uruchomić i mi pasuje idealnie (poza pytaniami o tryb odczytu,zapisu na dysku) gdyby nie ograniczenie do 4 wątków (tyle mam rdzeni).

Serio?

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.setmaxthreads.aspx - maksymalna ilość wątków, istnieje też metoda SetMinThreads.

http://msdn.microsoft.com/en-us/library/dd781401 - ForEach "z opcjami", co prowadzi do http://msdn.microsoft.com/en-us/library/system.threading.tasks.paralleloptions.maxdegreeofparallelism

Poza tym nawet mając tylko 4 wątki można sobie poradzić, po prostu podzielić problem na mniejsze (różne) zadania i wpakować w kolejkę.

0

Wszystkie podane linki widziałem ale nie ma tam kompletnie żadnych przykładów, szukam takich gdzie było by widać ze ktoś ustawia XX wątków i mu leci. Wszędzie tylko przykłady: 2 watki i random sleep ;D

Przykład z ParallelOptions.MaxDegreeOfParallelism pokazuje co sekunde 5 wierszy zamiast 20 :) co jest nie tak?

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication4
{
    using System.Threading;
    using System.Threading.Tasks;

    class Program
    {
        public static void DownloadFile(string url)
        {
            Console.WriteLine("A");
            Thread.Sleep(1000);
        }
        static void Main(string[] args)
        {
            var plik = File.ReadAllLines("C:/plik.txt"); // plik zawiera 30 wierszy

            Parallel.ForEach(plik, new ParallelOptions { MaxDegreeOfParallelism = 20 }, DownloadFile);
        }
    }
}
0

bo to jest MaxDegreeOfParallelism

The idea is that the system can use fewer threads than requested to process a loop.

The .NET thread pool adapts dynamically to changing workloads by allowing the number of worker threads for parallel tasks to change over time. At run time, the system observes whether increasing the number of threads improves or degrades overall throughput and adjusts the number of worker threads accordingly.

i faktyczna liczba wątków jest uzależniona od fizycznej ilości rdzeni i procesorów w komputerze
użyj ThreadPool jeśli chcesz mieć stałą liczbę wątków

0
LordNET napisał(a):
  • plik wejściowy jest duży wiec nie mogę go załadować całego :) muszę czytać linijka po linijce by nie otwierać go co wątek

Serio nie dajesz rady odczytać 30 MB na raz? 300 to bym jeszcze uwierzył, ale 30...

> - jak odpalę 20 wątków to jak wykryć ile ich zostało i jak uruchamiać kolejne by zawsze procowało ich np 50?

A masz 50 rdzeni? Bo jeśli nie, to wątki będą między sobą współdzieliły rdzenie i traciły niepotrzebnie czas na przełączanie między sobą (tzn. system operacyjny będzie to robił).

</p>

Proszę o pomoc jak to wykonać najprościej, urle/przykłady/filmiki bardzo będą pomocne.

  1. Wczytać plik do tablicy.
  2. Przetworzyć tablicę w tylu wątkach, można użyć ThreadPool, można Parallel.ForEach.
  3. Wynik umieszczać w tablicy.
  4. Całą tablicę zapisać do pliku.
    Można też nie wczytywać/zapisywać całości, lecz to jakoś buforować, np. po 1000 linii czy coś w tym stylu.
    Dodatkowo można się zastanowić, czy Regex jest prawidłowo użyty, oraz czy na pewno jest najszybszym rozwiązaniem.
0

No więc tak:
ThreadPool - udalo mi sie zrobic ale nie testowalem co sie bedzie dzialo i ile zajmie ramu jak w kolejce wstawie 30/300MB urli
Parallel.ForEach - nie nadaje sie bo nie mozna uruchomic wiekszej ilosci jednoczesnych watkow niz sie posiada rdzeni
regexp - jest jako parametr wejsciowy bo program ma byc uniwersalna wyszukiwarka potrzebnych mi ciagow

buforowanie - przy odczycie problem chwilowo odpada bo laduje calosc ;] przy zapisie jednak musze cos zrobic i sie zastanawiam czy cos takiego wystarczy:
watki wynik dopisuja do zmiennej
timer uruchamiany po kliknieciu szukaj co 2s sprawdza wielkosc zmiennej jezeli posiada > xxxxx znakow zapisuje zawartosc zmiennej do pliku i ja czysci. Jezeli progresbar osiagnie 100% timer sam sie wylacza ;]

Jeszcze mam problem z "Serwer spowodował naruszenie protokołu. Section=ResponseStatusLine" przy pobieraniu ponownie strony (ponowne klikanie szukaj) rozwiazaniem podobno jest SetAllowUnsafeHeaderParsing() z http://www.codeguru.pl/forum/watek/httpwebrequesthttpwebresponse-problem-z-filmweb,489030/489032/#489032 jednak kod zwraca blad przy linijce Assembly

Error 1 The type 'System.Configuration.ConfigurationSection' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. C:\Users\user\Documents\Visual Studio 2010\Projects\WindowsFormsApplication5\WindowsFormsApplication5\Form1.cs 28 13 WindowsFormsApplication5

0

Zamiast Timera użyłbym normalnego wątku, oczywiście trzeba pamiętać o synchronizacji tej zmiennej do zapisania między wątkami.
Co do błędu - zgodnie z sugestia z opisu, dodaj do projektu referencję do System.Configuration.

0

a ja bym po prostu od razu zapisywał na dysk
prosty lock na funkcję zapisującą i po problemie

to i tak zostanie zbuforowane przed system czy tam cache dysku i nie zostanie od razu fizycznie zapisane na dysk
tworzenie kolejnego bufora nie wiadomo po co jest robieniem kolejnego bufora nie wiadomo po co ;)

0

Wszystkim wielki dzięki za pomoc.
Mam jeszcze pytanie co jest nie tak z request.AddRange() bo za każdym razem jak bym nie kombinował dostaje źródło całej strony a chce zrobić ograniczenie by mój crawler nie zasysał jakichś przypadkowych plików po kilka MB :)

        private void button1_Click(object sender, EventArgs e)
        {
            HttpWebResponse response;

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(textBox1.Text);
            request.ServicePoint.ConnectionLimit = 100;

            request.AddRange("bytes", 0, 100);
            //request.AddRange(0, 100);
            //request.AddRange("bytes", (int)numericUpDown1.Value, (int)numericUpDown2.Value);
            
            response = (HttpWebResponse)request.GetResponse();

            Stream stream = response.GetResponseStream();
            StreamReader reader = new StreamReader(stream, Encoding.UTF8);
            String responseString = reader.ReadToEnd();

            richTextBox1.Text = responseString;
        }

1 użytkowników online, w tym zalogowanych: 0, gości: 1