
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.60] |
![]() |
|
![]() |
|
|
решил разобраться с вызовом функций WinApi в асинхронном режиме.
покурил msdn, накидал такой примерчик: ![]() ![]() procedure TForm1.DoAsyncLoad(); var lReaded: Cardinal; lTransferred: Cardinal; lBuff: Pointer; lRes: Boolean; lErr, lErr2: Cardinal; lFileHandle: Cardinal; lOverlapped: TOverlapped; begin lFileHandle := CreateFile( PChar( edtFileName.Text ), GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0 ); if lFileHandle = INVALID_HANDLE_VALUE then RaiseLastOSError; try lReaded := 0; ZeroMemory( @lOverlapped, SizeOf( TOverlapped )); GetMem( lBuff, C_BLOCK_SIZE ); //используем большой буфер try repeat lReaded := 0; //обнуляем внутренние данные для нового вызова "перекрываемой" функции lOverlapped.Internal := 0; lOverlapped.InternalHigh := 0; //hEvent не задаю, так как нигде ожидать ничего не будем lRes := ReadFile( lFileHandle, lBuff^, C_BLOCK_SIZE, lReaded, @lOverlapped ); if not lRes then lErr := GetLastError else lErr := ERROR_SUCCESS; if not lRes then begin //обрабатываем случаи, когда ReadFile = False case lErr of //проверка ощибки ReadFile ERROR_IO_PENDING: //асинхронная операция begin lTransferred := 0; repeat lRes := GetOverlappedResult( lFileHandle, lOverlapped, lTransferred, False ); if not lRes then lErr2 := GetLastError; if not lRes then case lErr2 of //проверка ошибки GetOverlappedResult ERROR_IO_INCOMPLETE: Application.HandleMessage; //операция не завершилась? обработаем очередное сообщение ERROR_HANDLE_EOF: begin //файл кончился? lReaded := 0; //ничего не прочитали break; //выходим из внутреннего цикла end; else RaiseLastOSError; end; until lRes; //пока не завершиться операция... if lRes then lReaded := lTransferred; //надо ли? end; ERROR_HANDLE_EOF: lReaded := 0; //если файл кончилсо, то выходим из цикла else RaiseLastOSError; end; end;//конец ветки, когда ReadFile = false if lRes then //здесь мы оказываемся в 2х случаях:1.ReadFile=true; 2.GetOverlappedResult в конце концов тоже вернул True begin // //что-то делаем с lBuff // lOverlapped.Offset := lOverlapped.Offset + lReaded; //сдвигаем end; until lReaded = 0; finally FreeMem( lBuff ); end; finally CloseHandle( lFileHandle ); end; end; все бы ничего... да только не всегда получается асинхронный вызов. в случае попытки чтения большого файла с винчестера функция FileRead ждет окончания чтения и возвращает true ( и только в случае первой попытки обращения к съемному носителю срабатывает часть кода по работе с GetOverlappedResult... объясните знающие люди, в чем соль?! |
Сообщ.
#2
,
|
|
|
неплохая статья про асинхронный ввод-вывод.
|
Сообщ.
#3
,
|
|
|
Ahilles Спасибо конечно, но ничего нового статья мне не рассказала. вся информация есть в MSDN.
да и примеры там все с флопом. |
Сообщ.
#4
,
|
|
|
Цитата MetalFan @ все бы ничего... да только не всегда получается асинхронный вызов. Вернее - работает не пойми как. ![]() Цитата MetalFan @ И?? Как же так - пытаешься использовать асинхронное чтение, а главный "семафор окончания отложенной операции" не создаешь? Далее :![]() ![]() //hEvent не задаю, так как нигде ожидать ничего не будем lRes := ReadFile( lFileHandle, lBuff^, C_BLOCK_SIZE, lReaded, @lOverlapped); Цитата MetalFan @ Это не ошибка - это признак того, что отложенная операция нормально стартовала. Здесь бы лучше запустить таймер и по его срабатыванию проверять - закончилась ли операция чтения (считано ли требуемое в ReadFile количество байт (WaitForSingleObject на эвент Overlapp'a должен вернуть WAIT_OBJECT_0 - если нет - то по GetOverlappedResult смотрим, сколько считано и сравниваем с размером файла, или по флагу ERROR_HANDLE_EOF принимаем решение сбросить дальнейшее чтение). Но лучше в любом случае перед считыванием получить размер файла, чтобы знать наверняка, сколько читать.) ![]() ![]() if not lRes then case lErr of //проверка ощибки ReadFile ERROR_IO_PENDING: //асинхронная операция |
Сообщ.
#5
,
|
|
|
Цитата medved_68 @ И?? Как же так - пытаешься использовать асинхронное чтение, а главный "семафор окончания отложенной операции" не создаешь? создавать и использовать его не обязательно. но даже его создание и использование положения не меняет. Цитата medved_68 @ Это не ошибка - это признак того, что отложенная операция нормально стартовала. отложенная операция удачно "стартовала" только если GetLastError вернул ERROR_IO_PENDING. да, это не ошибка по сути. Цитата medved_68 @ сколько считано и сравниваем с размером файла как ты заметил, файл читается не целиком, а кускакми з.ы. ты бы проверил работу кода, ибо пока ничего неясного ты не прояснил. |
Сообщ.
#6
,
|
|
|
M MetalFan, я ваш вопрос отредактировал, код не подсвечивался. Там в теге CODE было CODE=delphi, а нужно - CODE=pas, ну это так, на будущее. Что касается вопроса - думаю, не нужно вызывать Application.HandleMessage; так как эта процедура в конце вызывает засыпание потока на неопределенное время - пока в очередь не поступит сообщение (Idle из которого вызывается WaitMessage). Как обрабатывать сообщения и одновременно дожидаться окончания ввода/вывода гляньте здесь: DeviceIOControl Там вся соль в функции MsgWaitForMultipleObjects |
Сообщ.
#7
,
|
|
|
Rose Сорри, с винграда скопировал и не заметил ньюанса с pas.
по ссылке счас почитаю, спасибо Добавлено Цитата Rose @ так как эта процедура в конце вызывает засыпание потока возможно... но не в этом проблема... если отрабатывает часть кода при FileRead = 0(False), то все ок. видимо я не совсем ясно расписал ситуацию и ты меня не понял ![]() |
Сообщ.
#8
,
|
|
|
Цитата возможно... но не в этом проблема... Все же попробуй переделать свой код на менер того, как в приведенной ссылке - Event заведи, MsgWaitForMultipleObjects используй, ProcessMessages вместо HandleMessage, все должно работать. |
Сообщ.
#9
,
|
|
|
Rose Да нет! ты не понял... ветка кода с GetOverlappedResult и так работает на "ура"! но просто при чтении с ЖД FileRead всегда возвращает true.
при чем тут переделка кода? |
Сообщ.
#10
,
|
|
|
Цитата Rose Да нет! ты не понял... Видимо действительно не понял. Вторая попытка ![]() |
Сообщ.
#11
,
|
|
|
Rose можно на "ты"? ) а то "ты мне тут не выкайте")
Цитата Rose @ ReadFile не обязана вернуть код ошибки ERROR_IO_PENDING именно! в коде обрабатываются оба варианта операции... блин, наверное не очень его красиво структурировал... дополнил код комментариями. Цитата Rose @ Может у Вас именно этот случай? Или на ReadFile код надолго останавливается? у меня именно этот случай при работе с HDD!!!! как я уже ранее предположил, система скорее всего с жесткого считывает быстро, а вот мой 50Мб-йтный буфер заполняется как раз эти 0,5-1 секунды задержки... |
Сообщ.
#12
,
|
|
|
Цитата Rose можно на "ты"? ) а то "ты мне тут не выкайте") Привычка от других форумов, где принято на "Вы" ![]() Попробую твой код в ближайшее время, пока сам не увижу - наверное не пойму в чем проблема. |
Сообщ.
#13
,
|
|
|
Rose угу, пасип.
да проблемы даже можно сказать и нет... Цитата MetalFan @ как я уже ранее предположил, система скорее всего с жесткого считывает быстро, а вот мой 50Мб-йтный буфер заполняется как раз эти 0,5-1 секунды задержки... |
Сообщ.
#14
,
|
|
|
Цитата как я уже ранее предположил, система скорее всего с жесткого считывает быстро, а вот мой 50Мб-йтный буфер заполняется как раз эти 0,5-1 секунды задержки... В общем, мои исследования привели меня к выводу, что все дело в кешировании файлов. Если флаг FILE_FLAG_NO_BUFFERING не установлен, то первое обращение к файлу может занять несколько большее время, а последующие происходят значительно быстрее. Если установлен - то механиз кеширования не применяется, однако при этом нужно соблюсти некоторые требования, о которых в справке написано. Короче, то, что у тебя происходит - это нормальное поведение. |
Сообщ.
#15
,
|
|
|
Цитата Rose @ Если флаг FILE_FLAG_NO_BUFFERING не установлен проверял и с ним и без него. но еще один ньюанс у меня возник (с FILE_FLAG_NO_BUFFERING) - получаю Invalid Parameter вместо ERROR_HANDLE_EOF по достижению конца файла... Добавлено брр... возможно я не учел замечания в msdn по поводу выделения памяти под буфер |
![]() |
Сообщ.
#16
,
|
|
не только памяти, там еще выравнивание по размеру сектора...
|
Сообщ.
#17
,
|
|
|
Цитата но еще один ньюанс у меня возник (с FILE_FLAG_NO_BUFFERING) - получаю Invalid Parameter Ну там сказано в справке, что буфер должен быть выровнен и порции считывания/записи должны быть кратны размеру сектора. У тебя это требование соблюдено? Цитата FILE_FLAG_NO_BUFFERING Instructs the operating system to open the file with no intermediate buffering or caching. This can provide performance gains in some situations. An application must meet certain requirements when working with files opened with FILE_FLAG_NO_BUFFERING: · File access must begin at byte offsets within the file that are integer multiples of the volume's sector size. · File access must be for numbers of bytes that are integer multiples of the volume's sector size. For example, if the sector size is 512 bytes, an application can request reads and writes of 512, 1024, or 2048 bytes, but not of 335, 981, or 7171 bytes. · Buffer addresses for read and write operations must be aligned on addresses in memory that are integer multiples of the volume's sector size. One way to align buffers on integer multiples of the volume sector size is to use VirtualAlloc to allocate the buffers. It allocates memory that is aligned on addresses that are integer multiples of the operating system's memory page size. Since both memory page and volume sector sizes are powers of 2, this memory is also aligned on addresses that are integer multiples of a volume's sector size. An application can determine a volume's sector size by calling the GetDiskFreeSpace function. Rouse_ меня опередил ![]() |