在Windows中的python中终止进程之前如何保存对象?

时间:2018-09-07 15:21:47

标签: python multiprocessing

我想在终止过程之前保存所有数据。我正在使用基于Windows的计算机。如果不终止该过程,它将很好地工作。我尝试使用signal库,但是它仅适用于UNIX计算机。这是因为this。因此,基本上问题是要在Windows中截取我不怎么做的信号。使用库atexit也无济于事。我什至尝试制作方法save_stuff,但这也无济于事。有谁知道怎么做?

主要任务是在一段时间后停止程序执行并收集进程外所有可用数据。

from multiprocessing import Queue, Process

class Worker:

    def __init__(self):
        self.workers = 1

    def work(self, n):
        for i in range(n):
            self.workers = i
            print(i)

    def __str__(self):
        return str(self.workers)


class MyProcess(Process):

    def __init__(self, n):
        self.worker = Worker()
        self.shared_obj = Queue()
        self.shared_obj.put(self.worker)
        self.args = n
        super().__init__()

    def run(self):

        self.worker.work(self.args)

        self.shared_obj.get(self.worker)
        self.shared_obj.put(self.worker)

    def save_stuff(self):

        self.shared_obj.get(self.worker)
        self.shared_obj.put(self.worker)
        print('collect data')


if __name__ == '__main__':
    p = MyProcess(1000000)

    p.start()
    p.join(1)

    if p.is_alive():

        p.save_stuff()

        p.terminate()
        print('killed worker')
        print('shared object ' + str(p.shared_obj.get()))

    else:
        print('he was in time this worker')
        print('shared object ' + str(p.shared_obj.get()))

2 个答案:

答案 0 :(得分:0)

从父进程调用p.save_stuff()将不起作用。这两个进程在不同的地址空间中运行,并且父进程在子进程中不会具有已修改属性的更新副本。由于您要更新子级的数据并从父级读取数据,因此使用共享内存是安全的,该共享内存应在每次循环迭代时进行更新。以下代码应该可以满足您在此处想要实现的目标。

from multiprocessing import Value, Process

class Worker:

    def __init__(self):
        self.workers = 1

    def work(self, n, shm):
        for i in range(n):
            self.workers = i
            shm.value = self.workers
            print(i)

    def __str__(self):
        return str(self.workers)


class MyProcess(Process):

    def __init__(self, n, shm):
        self.worker = Worker()
        self.args = n
        self.shm = shm
        super().__init__()


    def run(self):
        self.worker.work(self.args, self.shm)



if __name__ == '__main__':
    shm = Value("i", 0)
    p = MyProcess(1000000, shm)
    p.start()
    p.join(1)

    if p.is_alive():
        p.terminate()
        print('killed worker')          
        print('shared object ' + str(shm.value))

    else:
        print('he was in time this worker')
        print('shared object ' + str(shm.value))

答案 1 :(得分:0)

在您self.worker.work(self.args)中的代码run中,它一直处于阻塞状态 完成整个循环。如果您只是终止流程,则发送的部分 将任何东西退还给父母将永远不会运行。

相反,我们需要一种让进程正常完成的方法,以便它可以发回 父对象的对象。 worker.run不允许为此屏蔽 我下面的代码将其包装在额外的线程中。子进程中的主线程 启动此线程并运行while循环,检查通过 管道并检查工作线程是否仍然存在。如果您的 工人自然完蛋或父母送出“毒丸”。当这个 可能会发生保存和发送,并且父级可以.get()实例。

import time
import logging
from threading import Thread
from multiprocessing import Process, Pipe


def init_logging(level=logging.DEBUG):
    fmt = '[%(asctime)s %(levelname)-8s %(processName)s' \
          ' %(funcName)s()] --- %(message)s'
    logging.basicConfig(format=fmt, level=level)


class Worker:

    def __init__(self, n):
        self.n = n

    def run(self):
        for i in range(int(self.n)):
            self.n -= 1
        return self

    def __str__(self):
        return f'{self.n}'

    def __repr__(self):
        return f'Worker(n={self.n})'


class MyProcess(Process):

    def __init__(self, n, log_level=logging.DEBUG):
        super().__init__()
        self.args = n
        self.log_level = log_level

        self.worker = None
        self.worker_thread = None
        self.parent_conn, self.child_conn = Pipe()

        logging.getLogger().debug('process instantiated')

    def run(self):
        init_logging(self.log_level)
        logging.getLogger().debug('process started')
        self.worker = Worker(self.args)
        self.worker_thread = Thread(target=self.worker.run)
        self.worker_thread.daemon = True
        self.worker_thread.start()

        while not self.child_conn.poll() and self.worker_thread.is_alive():
            self.worker_thread.join(0.5)  # heartbeat for checking
        self._save()

    def _save(self):
        """Send worker instance to parent."""
        logging.getLogger().debug('sending instance to parent')
        self.child_conn.send(self.worker)
        self.child_conn.close()

    def close(self):
        """Close process and receive result."""
        logging.getLogger().debug('closing process')
        # The actual value we are sending to child does not matter because
        # the while loop in `run` will break upon receipt of any object.
        self.parent_conn.send('POISON')

    def get(self):
        """Get result from child."""
        logging.getLogger().debug('get result from child')
        self.worker = self.parent_conn.recv()
        return self.worker

我在Linux下进行了测试,但将start_method设置为“ spawn”,在Windows上是默认设置,因此我希望它能够运行。

if __name__ == '__main__':

    init_logging()
    logger = logging.getLogger()

    p = MyProcess(100e6)  # try 10 vs 100e6 to toggle behaviour

    p.start()
    p.join(2)

    if p.is_alive():
        p.close()
        p.get()
        logger.info('killed worker')
        time.sleep(0.1)  # just to keep stdout in order
        print('shared object ' + repr(p.worker))

    else:
        p.get()
        logger.info('worker was in time')
        time.sleep(0.1)  # just to keep stdout in order
        print('shared object ' + repr(p.worker))

    assert isinstance(p.worker, Worker)
    p.join()

示例输出:

[2018-09-08 05:27:46,316 DEBUG    MainProcess __init__()] --- process instantiated
[2018-09-08 05:27:46,370 DEBUG    MyProcess-1 run()] --- process started
[2018-09-08 05:27:48,321 DEBUG    MainProcess close()] --- closing process
[2018-09-08 05:27:48,322 DEBUG    MainProcess get()] --- get result from child
[2018-09-08 05:27:48,396 DEBUG    MyProcess-1 _save()] --- sending instance to parent
[2018-09-08 05:27:48,402 INFO     MainProcess <module>()] --- killed worker
shared object Worker(n=82683682.0)

Process finished with exit code 0

注意worker.n.close()通话之前的2秒钟内从100M减少到82.68M。