Прочитать файл, используя темы - PullRequest
0 голосов
/ 31 августа 2018

Я пытаюсь написать программу на python, которая отправляет файлы с одного компьютера на другой, используя сокеты python. Но когда размер файла увеличивается, это занимает много времени. Можно ли читать строки файла последовательно, используя потоки?

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

Ответы [ 2 ]

0 голосов
/ 31 августа 2018

Во-первых, если вы хотите максимально ускорить процесс без использования потоков, чтение и отправка строки за раз могут быть довольно медленными. Python отлично справляется с буферизацией файла, чтобы дать вам строку для чтения, но затем вы отправляете крошечные 72-байтовые пакеты по сети. Вы хотите попытаться отправить по крайней мере 1,5 КБ за один раз, когда это возможно.

В идеале вы хотите использовать метод sendfile. Python сообщит ОС, что нужно отправить весь файл через сокет любым способом, который наиболее эффективен, без какого-либо участия вашего кода. К сожалению, это не работает в Windows; если вы заботитесь об этом, вы можете перейти к собственным API 1 напрямую с помощью pywin32 или переключиться на сетевую библиотеку более высокого уровня, такую ​​как twisted или asyncio.


А как насчет потоков?

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


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

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

Во-первых, вы хотите выполнить двоичное чтение фрагментов фиксированного размера. Вам нужно будет поэкспериментировать с различными размерами - может быть, 4 КБ быстрее, может быть 1 МБ ... поэтому убедитесь, что вы задаете его как константу, которую вы легко можете изменить в одном месте кода.

Далее, вы хотите иметь возможность отправлять данные как можно скорее, а не сериализовать. Это означает, что вы должны отправлять какой-то идентификатор, например смещение в файл, перед каждым фрагментом.

Функция будет выглядеть примерно так:

def sendchunk(sock, lock, file, offset):
    with lock:
        sock.send(struct.pack('>Q', offset)
        sent = sock.sendfile(file, offset, CHUNK_SIZE)
        if sent < CHUNK_SIZE:
            raise OopsError(f'Only sent {sent} out of {CHUNK_SIZE} bytes')

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

Тогда принимающая сторона может просто зациклить чтение 8 + CHUNK_SIZE байтов, распаковывая смещение, ища и записывая байты.


1. См. TransmitFile - но чтобы использовать это, вы должны знать, как переходить между объектами socket уровня Python и HANDLE s уровня Win32 и т. Д .; если вы никогда этого не делали, то есть кривая обучения - и я не знаю хорошего учебника, чтобы начать вас ..

2. Если вам действительно повезло, и, скажем, чтение файлов происходит только в два раза быстрее, чем запись в сокет, вы можете получить ускорение на 33% за счет конвейерной обработки, то есть только один поток может писать одновременно, но потоки, ожидающие записи, в основном уже закончили чтение, так что, по крайней мере, вам не нужно ждать там.

0 голосов
/ 31 августа 2018

Не темы.

source_path = r"\\mynetworkshare"
dest_path = r"C:\TEMP"
file_name = "\\myfile.txt"

shutil.copyfile(source_path + file_name, dest_path + file_name)

https://docs.python.org/3/library/shutil.html

Shutil предлагает функцию копирования высокого уровня, которая использует слой ОС для копирования. Это ваш лучший выбор для этого сценария.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...