использовать asyncio и aiofiles для генерации плоских файлов асинхронно не работает идеально - PullRequest
0 голосов
/ 16 сентября 2018

Я изучаю асинхронное программирование на python, и зная, что asyncio - это последний пакет, который мы должны использовать, поэтому я попытался написать простой скрипт для асинхронной генерации некоторых файлов (я использовал многопоточность для параллельной генерации файлов, и она отлично работает)).Так как запись IO стоит большую часть времени при создании файла, то, что здесь мой сценарий.

Версии

(py37) C:\Users\Hong\Desktop>
(py37) C:\Users\Hong\Desktop>pip freeze
aiofiles==0.4.0
asn1crypto==0.24.0
certifi==2018.8.24
cffi==1.11.5
chardet==3.0.4
cryptography==2.3.1
idna==2.7
pycparser==2.18
pyOpenSSL==18.0.0
PySocks==1.6.8
requests==2.19.1
six==1.11.0
urllib3==1.23
win-inet-pton==1.0.1
wincertstore==0.2

(py37) C:\Users\Hong\Desktop>python --version
Python 3.7.0

(py37) C:\Users\Hong\Desktop>

Асинхронный способ

import os
import asyncio
import aiofiles
import time
import datetime
import urllib

async def produce_content(c):
    return c*1000

async def create_file(file_name):
    tmp_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp.txt')
    try:
        async with aiofiles.open(tmp_file, mode='r') as rf:
            print('--> read start', datetime.datetime.now(), file_name)
            content = await rf.read()
            print('--> read end', datetime.datetime.now(), file_name)
            content = await produce_content(content)
            async with aiofiles.open(file_name, mode='w') as wf:
                print('--> write start', datetime.datetime.now(), file_name)
                await wf.write(content)
                await wf.flush()
                print('--> write end', datetime.datetime.now(), file_name)
    except Exception as e:
        print(e)
        raise e
    #return file_name

id = 0
async def my_action(file_name):
    global id
    id += 1
    local_id = id
    print('start to run %s'%local_id, datetime.datetime.now())
    await create_file(file_name)
    print('end to run %s'%local_id, datetime.datetime.now())

def run():
    files = [
        os.path.join(os.path.dirname(os.path.abspath(__file__)), 'f%s.txt'%i) for i in range(0,3)
    ]

    start_ts = datetime.datetime.now()
    print('start', start_ts)

    loop = asyncio.get_event_loop()
    tasks = [asyncio.ensure_future(my_action(f)) for f in files]
    try:
        loop.run_until_complete(asyncio.wait(tasks))
    finally:
        loop.close()

    end_ts = datetime.datetime.now()
    print('end', end_ts)
    print('time elapse', end_ts-start_ts)


if __name__=='__main__':
    run()

В моем примере,tmp.txt - это файл размером 240 КБ, я использовал его в качестве основы и создал целевой файл, который в 1000 раз больше его.Чтобы сравнить затраты времени между асинхронным и синхронным способом, вот синхронный способ, замените тело create_file на приведенное ниже (просто используйте обычный метод вместо aiofiles)

async def create_file(file_name):
    tmp_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp.txt')
    try:
        with open(tmp_file, mode='r') as rf:
            print('--> read start', datetime.datetime.now(), file_name)
            content = rf.read()
            print('--> read end', datetime.datetime.now(), file_name)
            content = produce_content(content)
            with open(file_name, mode='w') as wf:
                print('--> write start', datetime.datetime.now(), file_name)
                wf.write(content)
                print('--> write end', datetime.datetime.now(), file_name)
    except Exception as e:
        print(e)
        raise e

Есть результат

(py37) C:\Users\Hong\Desktop>python non_async.py
start 2018-09-16 22:33:12.929901
start to run 1 2018-09-16 22:33:12.929901
--> read start 2018-09-16 22:33:12.929901 C:\Users\Hong\Desktop\f0.txt
--> read end 2018-09-16 22:33:12.945520 C:\Users\Hong\Desktop\f0.txt
--> write start 2018-09-16 22:33:13.531200 C:\Users\Hong\Desktop\f0.txt
--> write end 2018-09-16 22:33:19.701563 C:\Users\Hong\Desktop\f0.txt
end to run 1 2018-09-16 22:33:19.831177
start to run 2 2018-09-16 22:33:19.831177
--> read start 2018-09-16 22:33:19.831177 C:\Users\Hong\Desktop\f1.txt
--> read end 2018-09-16 22:33:19.846803 C:\Users\Hong\Desktop\f1.txt
--> write start 2018-09-16 22:33:20.483649 C:\Users\Hong\Desktop\f1.txt
--> write end 2018-09-16 22:33:26.917791 C:\Users\Hong\Desktop\f1.txt
end to run 2 2018-09-16 22:33:27.073904
start to run 3 2018-09-16 22:33:27.073904
--> read start 2018-09-16 22:33:27.075903 C:\Users\Hong\Desktop\f2.txt
--> read end 2018-09-16 22:33:27.085896 C:\Users\Hong\Desktop\f2.txt
--> write start 2018-09-16 22:33:27.807891 C:\Users\Hong\Desktop\f2.txt
--> write end 2018-09-16 22:33:34.627992 C:\Users\Hong\Desktop\f2.txt
end to run 3 2018-09-16 22:33:34.746507
end 2018-09-16 22:33:34.762129
time elapse 0:00:21.832228

(py37) C:\Users\Hong\Desktop>
(py37) C:\Users\Hong\Desktop>
(py37) C:\Users\Hong\Desktop>
(py37) C:\Users\Hong\Desktop>python async.py
start 2018-09-16 22:33:50.945612
start to run 1 2018-09-16 22:33:50.948609
start to run 2 2018-09-16 22:33:50.953824
start to run 3 2018-09-16 22:33:50.953824
--> read start 2018-09-16 22:33:50.953824 C:\Users\Hong\Desktop\f0.txt
--> read start 2018-09-16 22:33:50.953824 C:\Users\Hong\Desktop\f1.txt
--> read start 2018-09-16 22:33:50.969449 C:\Users\Hong\Desktop\f2.txt
--> read end 2018-09-16 22:33:50.985078 C:\Users\Hong\Desktop\f0.txt
--> read end 2018-09-16 22:33:51.525238 C:\Users\Hong\Desktop\f1.txt
--> read end 2018-09-16 22:33:52.057857 C:\Users\Hong\Desktop\f2.txt
--> write start 2018-09-16 22:33:52.643887 C:\Users\Hong\Desktop\f0.txt
--> write start 2018-09-16 22:33:57.036816 C:\Users\Hong\Desktop\f1.txt
--> write start 2018-09-16 22:34:01.509756 C:\Users\Hong\Desktop\f2.txt
--> write end 2018-09-16 22:34:05.952100 C:\Users\Hong\Desktop\f0.txt
--> write end 2018-09-16 22:34:05.952100 C:\Users\Hong\Desktop\f1.txt
end to run 1 2018-09-16 22:34:06.105765
end to run 2 2018-09-16 22:34:06.206030
--> write end 2018-09-16 22:34:07.393667 C:\Users\Hong\Desktop\f2.txt
end to run 3 2018-09-16 22:34:07.525176
end 2018-09-16 22:34:07.525176
time elapse 0:00:16.579564

(py37) C:\Users\Hong\Desktop>

Асинхронный метод работает быстрее, чем синхронный метод (16,6 с против 21,8 с), но я ожидаю, что асинхронный метод должен выполняться быстрее ... Когда мы смотрим на журнал, мы видим чтение файла tmpна самом деле сделано очень близко.

--> read end 2018-09-16 22:33:50.985078 C:\Users\Hong\Desktop\f0.txt
--> read end 2018-09-16 22:33:51.525238 C:\Users\Hong\Desktop\f1.txt
--> read end 2018-09-16 22:33:52.057857 C:\Users\Hong\Desktop\f2.txt

Но начало записи не близко

--> write start 2018-09-16 22:33:52.643887 C:\Users\Hong\Desktop\f0.txt
--> write start 2018-09-16 22:33:57.036816 C:\Users\Hong\Desktop\f1.txt
--> write start 2018-09-16 22:34:01.509756 C:\Users\Hong\Desktop\f2.txt

Я ожидаю, что «начало записи» должно быть очень близко к «концу чтения» для каждой задачиПотому что производить_контент должно занимать совсем немного времени, но почему «запуск записи» для каждой задачи так по-разному?

Спасибо,

Хонг

...