You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多写多读场景下动态更新h5py虚拟数据集(VDS)失败的问题求助

多写多读场景下动态更新h5py虚拟数据集(VDS)失败的问题求助

我现在在尝试模拟并行创建多个H5文件,同时通过SWMR模式下的动态更新H5 VDS实时读取结果。每个H5文件处理同一数据集的不同分块,数据会被并行写入其中。我想监测文件大小的增长事件,以此触发VDS的更新。我试过实现类似的逻辑,但发现虚拟数据集的大小并没有增长,对应的实时图表也不更新。

目前看起来核心问题是虚拟源没有同步更新虚拟数据集——我能看到文件变化时watchdog确实触发了update_source方法,但VDS就是不更新。我接下来猜测可能需要所有文件在写入过程中保持打开状态,而且VDS管理器需要负责创建这些分块文件并保持打开,才能让虚拟源正常更新。

注:最终我会通过事件追踪数据来完成最终的重构,现在先想解决VDS能从变化的分块文件中正常更新的问题。

以下是我尝试的其中一版代码:

import numpy as np
import h5py
import multiprocessing as mp
import time
import matplotlib.pyplot as plt
import math
import os
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class VDSManager:
    def __init__(self, num_files, vds_filename="vds.h5", directory="."):
        self.num_files = num_files
        self.vds_filename = vds_filename
        self.directory = directory
        self.UNLIM = h5py.h5s.UNLIMITED
        self.vds_file = h5py.File(vds_filename, "w", libver="latest")
        self.vds_file.swmr_mode = True
        self.create_layout()
        self.create_vds()
        self.observer = Observer()
        self.handler = self.VDSHandler(self)
        self.observer.schedule(self.handler, self.directory, recursive=False)
        self.observer.start()
        print("VDS Manager started and VDS created.")

    def create_layout(self):
        fname = "output_1.h5"
        if os.path.exists(fname):
            with h5py.File(fname, "r", swmr=True) as f:
                size = f["data"].shape[0]
        else:
            size = 0
        self.virtual_layout = h5py.VirtualLayout((self.num_files, size),
                                                 maxshape=(self.num_files, None),
                                                 dtype='float64')

    def create_vds(self):
        for i in range(self.num_files):
            fname = f"output_{i}.h5"
            if os.path.exists(fname):
                with h5py.File(fname, "r", swmr=True) as f:
                    size = f["data"].shape[0]
            else:
                size = 0
            print(size)
            vsource = h5py.VirtualSource(fname, "data", shape=(size,), maxshape=(None,))
            self.virtual_layout[i, :self.UNLIM] = vsource[:self.UNLIM]
        self.vds_file.create_virtual_dataset("vdata", self.virtual_layout, fillvalue=np.nan)

    def update_source(self, file_index):
        vs = self.vds_file['vdata'].virtual_sources()
        vs[file_index].refresh()
        vs.flush()
        print(self.vds_file['vdata'][:].shape)

    def create_source(self, file_index):
        print("File created but I don't think I actually need to do anything!")

    class VDSHandler(FileSystemEventHandler):
        def __init__(self, manager):
            super().__init__()
            self.manager = manager

        def on_modified(self, event):
            if event.is_directory:
                return
            filename = os.path.basename(event.src_path)
            if filename.startswith("output_") and filename.endswith(".h5"):
                try:
                    idx = int(filename.split("_")[1].split(".")[0])
                    print(f"Detected modification in {filename}")
                    self.manager.update_source(idx)
                except Exception as e:
                    print("Error processing modified event:", e)

        def on_created(self, event):
            if event.is_directory:
                return
            filename = os.path.basename(event.src_path)
            if filename.startswith("output_") and filename.endswith(".h5"):
                try:
                    idx = int(filename.split("_")[1].split(".")[0])
                    print(f"Detected creation of {filename}")
                    self.manager.create_source(idx)
                except Exception as e:
                    print("Error processing created event:", e)

    def close(self):
        self.observer.stop()
        self.observer.join()
        self.vds_file.close()
        print("VDS Manager closed.")


def worker(file_index, task_queue):
    """
    Each worker writes its processed data to its own file.
    """
    filename = f"output_{file_index}.h5"
    print(f"Worker {file_index}: Opening {filename} for writing.")
    chunk = 0
    with h5py.File(filename, "w", libver="latest") as f:
        # create a dataset with unlimited growth axis
        dset = f.create_dataset("data", (0,), maxshape=(None,), dtype="float64")
        f.swmr_mode = True
        while True:
            print(f"Working on chunk {chunk} for file {file_index}")
            task = task_queue.get()
            if task is None:
                print(f"Worker {file_index}: Terminating.")
                break
            processed_chunk = np.sin(task)
            n = processed_chunk.shape[0]
            old_size = dset.shape[0]
            new_size = old_size + n
            dset.resize((new_size,))
            dset[old_size:new_size] = processed_chunk
            print(f"dataset size for dataset {file_index}: {dset.shape}")
            try:
                f.flush() # Flush updates so SWMR readers see them.
            except Exception as e:
                print(f"Worker {file_index}: flush failed: {e}")
            file_size = os.path.getsize(filename)
            print(f"Worker {file_index}: File size is {file_size} bytes.")
            print(f"Worker {file_index}: Wrote chunk of {n} elements.")
            time.sleep(0.1)
            chunk += 1


def live_plot_vds(vds, poll_interval=0.1):
    plt.ion()
    fig, ax = plt.subplots()
    line, = ax.plot([], [], "b-", lw=2)
    ax.set_xlabel("Index")
    ax.set_ylabel("sin(value)")
    ax.set_title("Live VDS Data")
    while True:
        time.sleep(poll_interval)
        try:
            ds = vds.vds_file['vdata']
            ds.id.refresh()
            data = ds[:]
            print(data.shape)
        except Exception as e:
            print("Error reading VDS:", e)
            data = np.array([])
        if data.size > 0:
            flat_data = data.flatten()
            x = np.arange(flat_data.size)
            line.set_data(x, flat_data)
            ax.set_xlim(0, flat_data.size)
            ax.set_ylim(-1.1, 1.1)
            fig.canvas.draw()
            fig.canvas.flush_events()
        if not plt.fignum_exists(fig.number):
            print("Plot window closed. Exiting live plot.")
            break
    plt.ioff()
    plt.show()


def main():
    N = 100_000         # Total number of data points.
    chunk_size = 1000   # Size of each chunk.
    NUM_FILES = 4       # Number of worker processes / output files.
    data = np.linspace(0, 10 * np.pi, N)

    # Delete existing VDS and output files before starting.
    files_to_delete = ["vds.h5"] + [f"output_{i}.h5" for i in range(NUM_FILES)]
    for fname in files_to_delete:
        if os.path.exists(fname):
            os.remove(fname)
            print(f"Deleted existing file: {fname}")

    # Launch worker processes.
    queues = [mp.Queue() for _ in range(NUM_FILES)]
    processes = []
    for i in range(NUM_FILES):
        p = mp.Process(target=worker, args=(i, queues[i]))
        p.start()
        processes.append(p)

    # Dispatch data chunks in round-robin order.
    num_chunks = math.ceil(N / chunk_size)
    for i in range(num_chunks):
        chunk = data[i * chunk_size: (i + 1) * chunk_size]
        file_idx = i % NUM_FILES
        queues[file_idx].put(chunk)
        print(f"Main: Sent chunk {i} (size {chunk.shape[0]}) to worker {file_idx}.")

    # Create the VDSManager (which opens the VDS file and starts watchdog).
    vds_manager = VDSManager(NUM_FILES, vds_filename="vds.h5", directory=".")

    # Start live plotting.
    live_plot_vds(vds=vds_manager, poll_interval=0.1)

    # Signal termination to each worker.
    for q in queues:
        q.put(None)

    # Wait for workers to finish.
    for p in processes:
        p.join()

    vds_manager.close()
    print("All workers finished.")


if __name__ == "__main__":
    main()

备注:内容来源于stack exchange,提问作者Amanda.py

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 18:59:29