2015-10-23 5 views
0

Я использую .NET 4.0 и имеют следующий код:Task.FromAsync и две нити

var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, 
        FileOptions.Asynchronous | FileOptions.SequentialScan); 

var buffer = new byte[bufferSize]; 

Debug.Assert(stream.IsAsync, "stream.IsAsync"); 

var ia = stream.BeginRead(buffer, 0, buffer.Length, t => 
{ 
    var ms = new MemoryStream(buffer); 
    using (TextReader rdr = new StreamReader(ms, Encoding.ASCII)) 
    { 

     for (uint iEpoch = 0; item < FileHeader.NUMBER_OF_ITEMS; item++) 
     { 

      dataList.Add(epochData); 
     } 
    } 

}, null); 

return Task<int>.Factory.FromAsync(ia, t => 
{ 

    var st = stream; 
    var bytes1 = st.EndRead(t); 
    var a = EpochDataList.Count; 
    var b = FileHeader.NUMBER_OF_EPOCHS; 
    Debug.Assert(a == b); 
    st.Dispose(); 
    return bytes1; 
}); 

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

Создает задачу, которая выполняет функцию методы конечной, когда указанный IAsyncResult завершается.

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

Между тем этот код прекрасно работает:

return Task<int>.Factory.FromAsync(stream.BeginRead, (ai) => 
{ 
    var ms = new MemoryStream(buffer); 
    using (TextReader rdr = new StreamReader(ms, Encoding.ASCII)) 
    { 

     using (TextReader rdr = new StreamReader(ms, Encoding.ASCII)) 
     { 

      for (uint iEpoch = 0; item < FileHeader.NUMBER_OF_ITEMS; item++) 
      { 

       dataList.Add(epochData); 
      } 
     } 
    } 
    stream.Dispose(); 
    return stream.EndRead(ai); 
}, buffer, 0, buffer.Length, null); 

Кроме того, я должен упомянуть, что возвращается задача используется в продолжении.

Заранее спасибо.

+1

Вы используете поток неправильно. 'EndRead' сообщает вам, сколько байтов было * на самом деле * прочитано.Я бы настоятельно предположил, что вам нужно вызвать этот метод * до *, вы можете что-либо сделать с помощью 'buffer', так как он сообщает вам, сколько байтов в' buffer' действительно действительно. –

ответ

2

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

Ваш наиболее насущной проблемой является то, что функция, которую вы предоставляете BeginRead не является частью IAsyncResultна всех. Таким образом, when a specified IAsyncResult completes не говорит о вашем обратном вызове, он говорит только о базовой асинхронной операции - вы получаете два отдельных обратных вызова, запущенных одним и тем же событием.

Теперь, для других задач:

  • Вам нужно, чтобы держать выпуск BeginRead сек снова и снова, пока EndRead возвращает 0. В противном случае, вы только когда-либо читает весь буфер в большинстве - если ваш файл длиннее этого, вы не собираетесь читать весь файл.
  • Вы комбинируете асинхронные обратные вызовы API старой школы с асинхронностью на основе задач. Это обязательно вызовет у вас проблемы. Просто научитесь правильно использовать Задачи, и вы обнаружите, что обратные вызовы на 100% не нужны.
  • EndRead сообщает, сколько байтов было фактически прочитано в предыдущей операции BeginRead - вы игнорируете эту информацию.

Выполнение этого правильно не так просто - если возможно, я предлагаю обновить до .NET 4.5 и воспользоваться ключевым словом await. Если это невозможно, вы можете установить пакет асинхронного таргетинга, который добавляет await в 4.0 как простой пакет NuGet.

С await, читая весь файл так же просто, как

using (var sr = new StreamReader(fs)) 
{ 
    string line; 

    while ((line = await sr.ReadLineAsync(buffer, 0, buffer.Length)) > 0) 
    { 
    // Do whatever 
    } 
} 
+0

Спасибо за ответ! Как я и предполагал. мои предположения были совершенно неправильными. Спасибо, что разъяснил это. Что связано с чтением файла: я читаю файлы около 500 КБ, поэтому я просто загружаю их в память, поэтому мне нужно всего лишь 1 операцию beginread. Я имею дело с асинхронным API, потому что я хочу использовать потоки портов завершения ввода-вывода, а не для отходов рабочих потоков. Если я буду использовать Task для операций чтения, я буду использовать рабочие потоки. – Sharov

+1

@Sharov Загрузка всего файла за один проход сложна - для одного вы выделяете буфер на кучу больших объектов, что может вызвать проблемы с фрагментацией кучи. Во-вторых, память должна быть привязана к длительности запроса, что может усложнить сбор мусора, когда не используется LOH. «Задача» не имеет ничего общего с рабочими потоками - только «Task.Run» делает. Когда вы используете, например, 'ReadLineAsync', вы используете те же потоки IOCP, что и для' BeginRead' - у вас есть только лучший API. – Luaan

+0

Спасибо за баллы за память! Я определенно буду их рассматривать. Я не уверен, что вы правы в Task.StartNew и пуле потоков, потому что он принимает потоки из пула, за исключением опции LongRunning. И, к сожалению, ReadLineAsync недоступен для меня на .net 4.0. Вот почему я возился со старомодным асинхронным API ... – Sharov

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