2015-05-19 4 views
0

Я хочу, чтобы отдельные асинхронные потоки метода splitFile выполнялись так, чтобы задача стала быстрее, но ниже кода не работает. Когда я отлаживаю, он достигает линии RecCnt = File.ReadAllLines(SourceFile).Length - 1; и выходит. Пожалуйста помоги.Асинхронный вызов с использованием делегата

public delegate void SplitFile_Delegate(FileInfo file); 

static void Main(string[] args) 
{ 
    DirectoryInfo d = new DirectoryInfo(@"D:\test\Perf testing Splitter"); //Assuming Test is your Folder 
    FileInfo[] Files = d.GetFiles("*.txt"); //Getting Text files 

    foreach (FileInfo file in Files) 
    { 
     SplitFile_Delegate LocalDelegate = new SplitFile_Delegate(SplitFile); 

     IAsyncResult R = LocalDelegate.BeginInvoke(file, null, null); //invoking the method 
     LocalDelegate.EndInvoke(R); 

     } 
} 

private static void SplitFile(FileInfo file) 
{ 
    try 
    { 
     String fname; 
     //int FileLength; 
     int RecCnt; 
     int fileCount; 
     fname = file.Name; 
     String SourceFile = @"D:\test\Perf testing Splitter\" + file.Name; 


     RecCnt = File.ReadAllLines(SourceFile).Length - 1; 
     fileCount = RecCnt/10000; 

     FileStream fs = new FileStream(SourceFile, FileMode.Open); 
     using (StreamReader sr = new StreamReader(fs)) 
     { 
      while (!sr.EndOfStream) 
      { 
       String dataLine = sr.ReadLine(); 
       for (int x = 0; x < (fileCount + 1); x++) 
       { 
         String Filename = @"D:\test\Perf testing Splitter\Destination Files\" + fname + "_" + x + "by" + (fileCount + 1) + ".txt"; //test0by4 
         using (StreamWriter Writer = file.AppendText(Filename)) 
         { 
          for (int y = 0; y < 10000; y++) 
          { 
           Writer.WriteLine(dataLine); 
           dataLine = sr.ReadLine(); 
          } 
          Writer.Close(); 
         } 

        } 
       } 
      } 
    } 
    catch (Exception ex) 
    { 
      Console.WriteLine(ex.Message); 
    } 
} 
+1

Что вы имеете в виду «это выходит»? Исключение? Блокировка? – flq

+0

Выполняется успешно, но не дает результата (не разделяет файл). После этой строки он возвращается к главной функции, снова возвращается в файл Split со следующим именем файла и делает то же самое снова. – Mandar

+3

как побочное замечание: 'async! = Быстрее' вообще – Carsten

ответ

1

Вы не обязаны вызывать EndInvoke, и на самом деле все EndInvoke действительно ждет от возвращаемого значения. Поскольку SplitFile возвращает void, я предполагаю, что есть оптимизация, которая срабатывает, потому что вам не нужно ничего ждать, и она просто игнорирует ожидание. Для получения более подробной информации: C# Asynchronous call without EndInvoke?

Это означает, что использование Begin/EndInvoke, скорее всего, не будет более быстрым, чем последовательное программирование (и, скорее всего, будет медленнее), поскольку цикл for все еще сериализуется, и вы все еще используете итерация в последовательном порядке. Все, что изменилось, это то, что вы используете делегат, где он выглядит как один, не нужен.

Возможно, что вы хотели использовать Parallel.ForEach (MSDN: https://msdn.microsoft.com/en-us/library/dd992001(v=vs.110).aspx), который потенциально будет запускать итерации параллельно.

Редактировать: Как уже упоминалось, использование нескольких потоков в файловых операциях, скорее всего, не улучшит производительность, поскольку ваши файловые операции, вероятно, связаны с дисками. Главным преимуществом, которое вы могли бы получить от чтения/записи асинхронного файла, вероятно, будет разблокирование основного потока для обновления пользовательского интерфейса. Если вам нужен лучший ответ, вам нужно указать, что вы хотите, с «эффективностью».

+0

В идеале я хочу, чтобы все потоки разделения файлов выполнялись независимо и параллельно. – Mandar

-2

Попробуйте использовать TPL для введения обработки Асинхронный, что-то вроде:

static void Main(string[] args) 
{ 
    DirectoryInfo d = new DirectoryInfo(@"D:\test\Perf testing Splitter"); //Assuming Test is your Folder 
    FileInfo[] Files = d.GetFiles("*.txt"); //Getting Text files 

    List<Task> taskList = new List<Task>(); 

    foreach (FileInfo file in Files) 
    { 
     FileInfo localFi = file; 
     Task t = Task.Factory.StartNew(()=>SplitFile(localFi)); 
     taskList.Add(t);  
     } 

     //Wait for all Tasks to complete 
     Task.WaitAll(taskList.ToArray()); 
    } 

Вы можете даже использовать Parallel.ForEach для параллельной обработки каждого файла, ваше решение полностью зависит от того, что вы хотите достичь. Вам, безусловно, необходимо обеспечить защиту общих ресурсов с помощью конструкции синхронизации.

+2

Спиннинг нескольких потоков для выполнения операций FS IO, скорее всего, ухудшит производительность, а не увеличит их. Кроме того, нет причин для запуска потоков threadpool при выполнении async IO. –

+0

Async не гарантирует быструю работу, я никогда не упоминал об этом, это просто бесплатный интерфейс для долговременной работы. Не обязательно, чтобы мы создавали отдельный поток для каждого элемента цикла for, который может быть ограничен. В чем проблема с потоками пула потоков, другой вариант - это шаблон ожидания async, я не понимаю причину пониженного голосования, нет ничего плохого в коде/ответе, даже если это не самое оптимальное решение, подумайте о голосовании когда объяснение полностью неверно, а не при падении вывода –

+2

* В чем проблема с использованием потоков пула потоков * Проблема заключается в том, что вам не нужно использовать потоки пула потоков для выполнения async IO, что является полностью точкой , которые вы неправильно интерпретируете. Асинхронные шаблоны были созданы именно для того, чтобы перекрывать IO [нет потоков] (http://blog.stephencleary.com/2013/11/there-is-no-thread.html). Вы даете OP непротиворечивый пример, который приведет к ухудшению его кода в производительности и блокированию нескольких потоков threadpool. –

3

Ваш код не требует многопоточности. На самом деле даже не нужна асинхронная обработка - вы скорее всего насыщаете ввод-вывод, и если у вас нет нескольких дисков в качестве источников данных, вы не улучшите это, добавив параллелизм.

С другой стороны, ваш код читает каждый файл дважды. Без причины, тратя память, время и даже процессор. Вместо этого, просто сделать это:

FileStream fs = new FileStream(SourceFile, FileMode.Open); 
using (StreamReader sr = new StreamReader(fs)) 
{ 
    string line; 
    string fileName = null; 
    StreamWriter outputFile = null; 
    int lineCounter = 0; 
    int outputFileIndex = 0; 

    while ((line = sr.ReadLine()) != null) 
    { 
     if (fileName == null || lineCounter >= 10000) 
     { 
      lineCounter = 0; 
      outputFileIndex++; 
      fileName = @"D:\Output\" + fname + "_" + outputFileIndex + ".txt"; 

      if (outputFile != null) outputFile.Dispose(); 
      outputFile = File.AppendText(fileName); 
     } 

     outputFile.WriteLine(line); 
     lineCounter++; 
    } 
} 

Если вам действительно нужно иметь имя файла в формате XOutOfY, вы можете просто переименовать их потом - это намного дешевле, чем чтение исходного файла дважды, строку за строкой. Или, если вы не заботитесь о сохранении всего файла в памяти сразу, просто используйте массив, который вы получили от ReadAllLines, и повторите его, вместо того, чтобы делать все заново.

Чтобы сделать это еще проще, вы также можете использовать foreach (var line in File.ReadLines(fileName)).

Если вы действительно хотите сделать это асинхронным, способ справиться с этим, используя асинхронный ввод-вывод, а не только путем наматывания новых потоков. Таким образом, вы можете использовать await с StreamReader.ReadLineAsync и т. Д.

+0

Ошибка: не может использовать неназначенную переменную для linecounter и outputfile. – Mandar

+0

@Mandar Это исправлено. – Luaan

+0

Спасибо Luaan. Это сработало. То, что эти строки делают точно: if (outputFile! = Null) outputFile.Dispose(); outputFile = File.AppendText (имя_файла); – Mandar

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