@romvup

Как работать с общим списком в многопользовательском приложении?

Я написал простой пример (в обоих файлах один код, только разный ID), если запустить обе программы, все работает, но...

1.py
import os
import pickle
import random
import time

if not os.path.isfile('file'):
    with open('file', 'wb') as file:
        pickle.dump([], file)
    file.close()

ID = 1

while True:
    f = open('file', 'rb')
    li = pickle.load(f)
    f.close()
    print(li)
    x = random.randint(100, 999)
    for i in li:
        if i[0] == ID:
            i[1] = x
            break
    else:
        li.append([ID, x])

    f = open('file', 'wb')
    pickle.dump(li, f)
    f.close()
    time.sleep(1)
2.py
import os
import pickle
import random
import time

if not os.path.isfile('file'):
    with open('file', 'wb') as file:
        pickle.dump([], file)
    file.close()

ID = 2

while True:
    f = open('file', 'rb')
    li = pickle.load(f)
    f.close()
    print(li)
    x = random.randint(100, 999)
    for i in li:
        if i[0] == ID:
            i[1] = x
            break
    else:
        li.append([ID, x])

    f = open('file', 'wb')
    pickle.dump(li, f)
    f.close()
    time.sleep(1)

... нутро мне подсказывает, что так нельзя делать.. при большом количестве пользователей и при большом списке, данные будут частично затирать друг друга (тем более пока пакет дойдет от клиента серверу и обратно). Не прошу готовое решение, только объясните вкратце как такое решается? Что нужно изучать?
  • Вопрос задан
  • 681 просмотр
Решения вопроса 1
@antares4045
Точка входа в приложение (запускаемый python-скрипт) должна быть одна: при входе или запуске пользователь вводит свой id (или он генерируется на основании неких косвенных признаков). Для хранения данных необходимо использовать базу данных. причём, учитывая что вы сразу захотели многопользовательское приложение, необходимо поднять полноценный сервер базы данных (простенький sqlite не прокатит).
Если вы не знакомы с sql, рекомендую начать с знакомства с mongodb и способа взаимодействия с ним через python. Если всёже знакомы, то лучше будет поднять инстанс mysql/postgress/или что вы там знаете, вооружиться библиотекой pyodbc и начать постигать мир полноценного промышленного программирования.

UPD: ответив решил исследовать, нельзя ли стандартный sqlite3 запустить в мультипроцесном режиме -- оказалось костыль есть.

Подключение к базе блокирует другие подключения и они ждут, пока файл освоботится с задержкой timeout. так что если соединения постоянно открывать-зарывать, то сразу несколько процессов могут работать с одной базой (теоретически её можно коннект в picke положить, но это злая идея).
Вот такой код по идее должен справляется с парой десятков пользователей (а главное: никакого сервера):
import sqlite3
import os
import sys
import random
import time
from datetime import datetime

TIMEOUT_DELAY=10

CURRENT_DIRECTORY = os.path.normpath(
    os.path.relpath(
        os.path.dirname(__file__),
        os.getcwd())
    )
DB_PATH = os.path.normpath(os.path.join(CURRENT_DIRECTORY, './db/lists.db'))



def initBase():
    with sqlite3.connect(DB_PATH) as connect:
        cursor = connect.cursor()
        cursor.execute("""
CREATE TABLE lists(
    userid INTEGER primary key,
    value INTEGER
)
""")
        connect.commit()



if not os.path.exists(DB_PATH):
    os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
    initBase()


ID = None
if len(sys.argv) > 1:
    ID = int(sys.argv[1])
else:
    ID = int(datetime.now().timestamp() * 1000) % 10000 + 10000
    print(f'ID не передан. сгенерирован идентефикатор {ID}')


while True:
    with sqlite3.connect(DB_PATH, timeout=TIMEOUT_DELAY) as connect:
        cursor = connect.cursor()
        cursor.execute("""SELECT userid, value FROM lists""")
        print(cursor.fetchall())
        x = random.randint(100, 999)
        cursor.execute("""
INSERT INTO lists(value, userid)
VALUES (?, ?)
ON CONFLICT(userid) DO UPDATE SET
value=?
WHERE userid=?
""", [x, ID]*2)
        connect.commit()
    time.sleep(1)


открыл 3 консоли и запустил
python3 __main__.py
python3 __main__.py 1
python3 __main__.py 2
вроде работает):
616e00f12619c652105096.png

Но лучше всеравно так не делать, а использовать всёже нормальный сервер бд
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы