#Что произошло и когда проявляется ошибка
При одновременной записи несколькими процессами или потоками изменения файла могут теряться: каждый писатель читает старую версию, перезаписывает чужую запись либо конфликтует с другим процессом.
#Причины и диагностические признаки
| Возможная причина | Как узнать | Что делать |
|---|---|---|
Запись с режимом w в нескольких процессахopen(...,"w") обнуляет файл перед записью. | В файле остаётся результат только последнего задания | Разделить выходные файлы и объединять после завершения. |
| Чтение-изменение-запись без синхронизацииНесколько исполнителей меняют одну копию состояния. | Иногда пропадают инкременты и строки | Сделать единственного писателя или транзакционное хранилище. |
| Параллельная запись в один файлЗаписи процессов не согласованы на уровне приложения. | Строки перемешаны или повреждены | Использовать очередь результатов и один поток записи. |
#Подробная пошаговая диагностика и исправление
- 1. Сделайте резервную копию и воспроизведите на тестовом файлеСкопируйте исходный результат в отдельную папку. Определите, одновременно ли работают процессы, потоки или задачи планировщика; проверьте, не открывается ли один и тот же файл с режимом
wилиx. Не запускайте повторную проверку на боевой базе или файле с пользовательскими данными.
- 2. Найдите общий участок чтения и записиВ коде поищите
read_text()→ изменение строки →write_text(), а также несколькоopen(...,"a")из разных исполнителей. Даже если отдельный вызов записи кажется коротким, последовательность чтение–изменение–запись не становится автоматически безопасной.
- 3. Разделите выходы независимых задачДля пакетной обработки проще всего писать отдельный файл на каждую задачу, а после завершения объединять результаты строго в одном процессе. Пример демонстрирует уникальные имена по номеру задачи, не удаляя уже существующие файлы.
- Пример кода по темеPythonНомер задачи в реальном параллельном запуске должен быть уникальным;
xзапрещает перезапись.from pathlib import Path folder = Path("results") folder.mkdir(exist_ok=True) for job_id in range(3): with (folder / f"job-{job_id}.txt").open("x", encoding="utf-8") as f: f.write(f"job={job_id}\n")
- 4. Для потоков синхронизируйте общую операциюЕсли запись производится из потоков одного процесса, создайте общий
threading.Lockи охватите блокировкой весь участок изменения состояния; блокировка не синхронизирует отдельные процессы. Для нескольких процессов лучше очередь с одним писателем или SQLite с транзакциями и обработкой конфликтов. - Только для потоков одного процесса и общего объекта lockPythonТолько для потоков одного процесса и общего объекта lock.
import threading lock = threading.Lock() def append_result(line): with lock: with open("result.txt", "a", encoding="utf-8") as f: f.write(line + "\n")
- 5. Проверьте подсчёт и целостность после параллельного тестаНа небольшой копии запустите фиксированное число заданий, сравните количество уникальных записей, отсутствие повторов и предсказуемый итог. Применяя SQLite, используйте транзакцию, а не ручное переписывание общего JSON. Не считайте проверку успешной по одному случайному запуску.
#Как проверить, что проблема устранена
На серии воспроизводимых параллельных запусков число результатов совпадает с числом задач, нет потерянных или частично записанных строк.
#Если исправление не помогло
Для межпроцессной синхронизации не подставляйте threading.Lock: он действует только внутри процесса. На сетевых папках гарантии блокировок и атомарных операций могут различаться.