2016-02-17 4 views
0

Я конвертирую свое старое - наследие - веб-приложение, которое обрабатывает - анализирует - загруженные файлы. Он использует чистый javascript + MVC.MVC Javascript xmlhttprequest вызов метода async

/* javascript */ 
function Parse(files) { 
    var idFiles=document.getElementById('inputF').value; 
    CallServer('ProcessFile/Parse', idFiles, function (m) 
    { 
     if (m != null) 
     { 
     var om = JSON.parse(m); 
     MsgLog('Parse finished ' + om.msg); 
     } 
    }); 
} 

var CallServer = function (url, content, callback) { 
    var rqs = new XMLHttpRequest(); 
    rqs.open('POST', url, true); 
    rqs.onreadystatechange = function() { 
    if (rqs.readyState == 4 && rqs.status == 200) { 
     callback(rqs.responseText); 
     return true; 
    } else { return false; } 
    }; 
    rqs.send(content); 
} 

/* View */ 
... 
<a href="#" id="parseFiles" onclick="Parse()" title="Parse selected documents">Parse</a> 
... 
/* Controller */ 
public JsonResult Parse(string idFiles){ 
// parses input string and returns an array of int 
int[] idf=getIds(idFiles); 
string wholeFile=string.Empty; 
string parsedData=string.Empty; 
string outputFileName=string.Empty; 
List<string> doneFiles=new List<string>(); 
foreach(int f in idf) 
{ 
    wholeFile=ReadFile(f); 
    parsedData=ParseFile(wholeFile); 
    outputFileName=SaveParsed(parsedData); 
    doneFiles.Add(outputFileName); 
} 
return Json(new { doneFiles }); 
} 

Контроллер возвращает данные для просмотра списка разобранных файлов. Поскольку синтаксический анализ занимает много времени, я пытаюсь преобразовать этот код в асинхронный/ожидаемый код Task + Parallel (?). Я переписал метод Parse, но я немного запутался в async/wait Task + Parallel stuffs. Кроме того, я не знаю, нужно ли мне изменять javascript. Теперь контроллер выглядит следующим образом:

public JsonResult Parse(string idFiles){ 
int[] idf=getIds(idFiles); 
string wholeFile=string.Empty; 
string parsedData=string.Empty; 
string outputFileName=string.Empty; 
List<string> doneFiles=new List<string>(); 
    await Task.Run(() =>Parallel.ForEach(idf, async currFile => 
    { 
     wholeFile=ReadFile(currFile); 
     Task<string> parseData=ParseFile(wholeFile); 
     await Task.WhenAll(parseData); 
     Task<string> write = await parseData.ContinueWith(async (aa) => await SaveParsed(parsedData)); 
     Task.WhenAll(write); 
} 
return Json(new { doneFiles }); 
} 

Мой Desiderata был бы истинным асинхронной Parse метод, который после завершения обновления списка проанализированных файлов в окне ... file1 - разобраны file2 - разобраны file3 -

+3

И что конкретно представляет собой ваш вопрос? –

+0

Я не знаю, правильно ли я использую инструкции Task.Run и Parallel.ForEach, чтобы сделать метод Parse «async» правильным, и если мне нужно изменить также вызов javascript. – user1732337

ответ

0

Поскольку синтаксический анализ занимает много времени, я пытаюсь преобразовать этот код в асинхронный/ожидающий код задачи + параллельный (?) Код.

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

Например, если медленная часть взаимодействует с сервером, это может быть связано с использованием async - await. Если медленная часть - это код, привязанный к ЦП, Parallel.ForEach() может быть правильным решением.


Это, как говорится, ваш код содержит много ошибок:

  • await Task.Run(…): это просто переключает свой код для выполнения на другом потоке, это не улучшает производительность в любом случае. Единственное, что он делает, это добавить некоторые накладные расходы.

  • Parallel.ForEach(idf, async currFile => …: Parallel.ForEach() does not play nice with async-await. Используйте тот или иной, при необходимости, но не тот и другой.

  • wholeFile=ReadFile(currFile);: Вы определяете переменные за пределами цикла, что означает, что одна и та же переменная разделяется всеми итерациями цикла. Это нормально для простого foreach (хотя я бы даже подумал, что это плохой стиль), но он не будет работать правильно для параллельного кода, потому что потоки будут переписывать друг друга в переменных, вызывая запутывающие ошибки.

  • wholeFile=ReadFile(currFile); Task<string> parseData=ParseFile(wholeFile);: ReadFile звучит как операция с привязкой к IO, что означает, что имеет смысл сделать его асинхронным. ParseFile звучит как операция с привязкой к процессору, поэтому, вероятно, не имеет смысла делать его асинхронным.

  • await Task.WhenAll(parseData);: Используйте Task.WhenAll(), когда вы хотите await кратному Task s одновременно. У вас есть только один Task, поэтому вы можете использовать только await parseData.

  • await parseData.ContinueWith(async (aa) => await SaveParsed(parsedData)): Вообще говоря, async - await делает ContinueWith() устаревшим.Если вы хотите получить результат Task, использование await намного проще, чем ContinueWith().

  • Task.WhenAll(write);: Это не делает ничего, Task.WhenAll()возвращает в Task, который сочетает в себе несколько других Task с, он не делает ни ждать на своих собственных.

  • Вы никогда не добавляете к doneFiles, поэтому результат всегда будет пустым. Но вы не можете просто вызвать Add() изнутри параллельного цикла, потому что он не является потокобезопасным.

  • Чтобы использовать await, вам необходимо пометить свой метод как async и изменить тип возврата на Task.


Как вы могли бы сделать все это работать? Как я уже сказал, это зависит от характеристик производительности вашего кода. Итак, я собираюсь предположить, что чтение и запись файлов одновременно имеет смысл, или, по крайней мере, это не наносит вреда (может, особенно с обычными вращающимися жесткими дисками). И я также предполагаю, что параллельный анализ имеет смысл (может быть, нет, например, если ваш сервер всегда обрабатывает многие запросы).

Для этого простой вариант кода может выглядеть следующим образом:

private async Task<string> ParseOne(int fileId) 
{ 
    string wholeFile = await ReadFile(fileId); 
    string parsedData = ParseFile(wholeFile); 
    string outputFileName = await SaveParsed(parsedData); 
    return outputFileName; 
} 

public async Task<JsonResult> Parse(string idFiles) 
{ 
    int[] idf = getIds(idFiles); 

    var doneFiles = await Task.WhenAll(idf.Select(id => ParseOne(id))); 

    return Json(new { doneFiles }); 
} 

Более сложный вариант этого кода может быть более подходящим. Например, используя SemaphoreSlim для ограничения параллелизма. Или даже поток данных TPL для лучшего контроля над каждой частью процесса синтаксического анализа.

+0

Я оценил все ваши разъяснения и комментарии. Спасибо! Я собираюсь изменить свою реализацию. – user1732337

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