2013-06-05 2 views
1

У вас есть приложение, которое сортирует большой файл txt по некоторым условиям. Мне нужно запустить, например, 5 потоков, но я читаю строки за строкой из файла с помощью цикла foreach. И если я запустил 5 потоков с моим кодом, все потоки будут принимать одинаковые строки.Как сделать многопоточное приложение с циклом foreach

Вот мой код, где я начала 1 Тема:

Thread[] thr; 
    private void button1_Click(object sender, EventArgs e) 
    { 
     button1.Enabled = false; 
     button4.Enabled = true; 
     decimal value = 1; 
     int i = 0; 
     int j = (int)(value); 
     thr = new Thread[j]; 
     for (; i < j; i++) 
     { 
      thr[i] = new Thread(new ThreadStart(go)); 
      thr[i].IsBackground = true; 
      thr[i].Start(); 
     } 
    } 

    private static IEnumerable<string> ReadLineFromFile(TextReader fileReader) 
    { 
     using (fileReader) 
     { 
      string currentLine; 
      while ((currentLine = fileReader.ReadLine()) != null) 
      { 
       yield return currentLine; 
      } 
     } 
    } 


    public void go() 
    { 
     while (true) 
     { 
      TextReader readFile = new StreamReader(file_path, System.Text.Encoding.UTF8, true); 
      foreach (string line in ReadLineFromFile(readFile)) 
      { 
       if (line.Split(':')[0].Contains("@")) 
       { 
        string out_line = line.Split(':')[0].Replace("+", "") + ":" + line.Split(':')[1]; 
        lock (locker) 
        { 
         mail_count++; 
         log_mail(mail_count); 
         mail.Add(out_line.Trim().Replace(";", ":")); 
        } 
       } 
       else 
       { 
        string out_line = line.Split(':')[0].Replace("+", "") + ":" + line.Split(':')[1]; 
        lock (locker) 
        { 
         rubbish_count++; 
         log_rubbish(rubbish_count); 
         rubbish.Add(out_line.Trim()); 
        } 
       } 
      } 
      MessageBox.Show("Ready"); 
      BeginInvoke(
      new MethodInvoker(() => 
      { 
       button1.Enabled = true; 
       button4.Enabled = false; 
      })); 
      break; 
     } 
    } 
+2

Честно говоря, все, что вы делаете, вы жесткий диск может быть только в одном положении в данный момент времени, так что нет никакого реального смысла «распараллеливание msgstr "прочитанный файл. Вы могли бы, однако, дать смещение каждому потоку, из которого можно начать чтение в файле, но это не ускорит что-либо. ^^ – C4stor

+0

Если вы действительно хотите ускорить свою функцию, вам нужно использовать кэширование дисков в ваших интересах : начните чтение файла, прежде чем он действительно понадобится. Это автоматически загрузит файл в память (http://en.wikipedia.org/wiki/Page_cache), а затем вы сможете прочитать его из ОЗУ, когда это действительно необходимо. Тогда вы сможете полностью использовать шаблон производителя-потребителя Pako , – C4stor

ответ

3

Почему не происходит с обычной схеме производитель-потребитель? Создайте один файл для чтения нитей, поместите строки в какую-либо общую коллекцию, а другие потоки просто выбирают данные из коллекции и обрабатывают ее.

Подробнее - вы можете прочитать из файла и для каждой строки создать Task, который позаботится об обработке этой строки и поместит результат в выходную коллекцию.

Это кажется более чем 5 нитями, пытаясь прочитать один и тот же файл и не читая одну и ту же строку несколько раз.

+0

Это нормально, но чтение файла, вероятно, на порядок медленнее, чем процесс, который он делает с файлом, поэтому, вероятно, нет смысла делать это. – C4stor

+0

Потому что я загружаю txt-файл с миллионами строк, и если я так делаю, как вы сказали, я получу исключение outmemory – obdgy

+0

Я полностью согласен с вами.Я просто хотел дать понять, как решить проблемы simillar в целом. В этом конкретном случае такое решение не даст ожидаемых преимуществ - правда. – Pako

4

Невозможно использовать все потоки для чтения одного и того же файла, и чтение из общего файла затруднено и неэффективно.

В вашей основной функции, вам нужно будет что-то вроде:

Parallel.ForEach(System.IO.File.ReadLines(file_path, System.Text.Encoding.UTF8), 
    line => ProcessOneLine(line) 
); 

, а затем ProcessOneLine бы сделать .Split(':') и т.д.

+0

ProcessOneLine что это такое? – obdgy

+0

Вам нужно будет написать 'void ProcessOneLine (строка строки) {...}'. Сделать все необходимое для 1 линии. Threading предоставляется классом 'Parallel'. –

+0

@HenkHolterman Используя Parallel.ForEach, он прочитает весь файл (здесь будут миллионы строк). Использование BlockingCollection для реализации шаблона производителя-потребителя является альтернативным способом ИМО. – ValidfroM

0

Я хотел бы подтвердить и расширить то, что сказал Пако. Другие потоки должны использовать данные из общей коллекции, содержащей данные, и обрабатывать ее.

Наличие нескольких потоков доступа к текстовому файлу звучит как потенциальная возможность для условий гонки. По существу непредсказуемые результаты могут возникать, если поток изменяет файл, в то время как из него читает другой поток.

Я также испытал BSOD в прошлом при использовании нескольких потоков, которые обращаются к одному и тому же текстовому файлу и могут рекомендовать против него. Однако, если вы настаиваете на этом, я бы рекомендовал вам взглянуть на ключевое слово «lock» и шаблон дизайна singleton. Это позволит вам убедиться, что только один поток обращается к файлу за раз.

Ссылки по теме:

http://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.80).aspx http://en.wikipedia.org/wiki/Singleton_pattern

http://en.wikipedia.org/wiki/Double-checked_locking

Смежные вопросы