Ну давайте обратимся к документации и попробуем понять в какой момент происходят изменения в конечном файле:
ZipArchive::close — Close opened or created archive and save changes. This method is automatically called at the end of the script.
То есть либо при вызове ZipArchive::close, либо в конце скрипта (так как автоматически вызывается ZipArchive::close).
Сам по себе ZipArchive — синхронный.
Если в одном потоке, то вы создаете и редактируете один архив, через один объект ZipArchive и когда вызываете close — все сохраняется в файл, на диск.
Если вы работаете в нескольких потоках, то у вас создается несколько ZipArchive, которые друг о друге ничего не знают и когда вы вызываете close — каждый из них пытается сохранить свои изменения и здесь уже кто последний тот и
папа сохранил свою версию.
Нужно либо как-то передавать экземпляр ZipArchive между вашими потоками, либо писать свою (или найти готовую) библиотеку для асинхронной работы с zip архивами.
Как более простой вариант — делать работу с файлами асинхронно (во временной директории?), а затем их архивировать через тот же zip (консольную утилиту).