8

我正在尝试创建一个程序来启动一个进程池,比如说 5 个进程,执行一些操作,然后退出,但让 5 个进程保持打开状态。稍后,用户可以再次运行该程序,而不是启动新进程,而是使用现有的 5。基本上它是一个生产者-消费者模型,其中:

  1. 生产者的数量不同。
  2. 消费者的数量是恒定的。
  3. 生产者可以由不同的程序甚至不同的用户在不同的时间启动。

我正在使用multiprocessing目前在 Python 2.6.4 中的内置模块,但打算最终迁移到 3.1.1。

这是一个基本的使用场景:

  1. 开始状态 - 没有进程在运行。
  2. 用户启动program.py operation- 一个生产者,五个消费者运行。
  3. 操作完成 - 五个消费者正在运行。
  4. 用户启动program.py operation- 一个生产者,五个消费者运行。
  5. 用户启动program.py operation- 两个生产者,五个消费者运行。
  6. 操作完成 - 一个生产者,五个消费者运行。
  7. 操作完成 - 五个消费者正在运行。
  8. 用户启动program.py stop并完成 - 没有进程运行。
  9. 用户启动program.py start并完成 - 五个消费者正在运行。
  10. 用户启动program.py operation- 一个采购员,五个消费者运行。
  11. 操作完成 - 五个消费者正在运行。
  12. 用户启动program.py stop并完成 - 没有进程运行。

我遇到的问题是我不知道从哪里开始:

  1. 检测消费者进程正在运行。
  2. 从以前不相关的程序中访问它们。
  3. 以跨平台的方式执行 1 和 2。

一旦我能做到这一点,我就知​​道如何管理流程。必须有一些可靠的方法来检测现有进程,因为我已经看到 Firefox 这样做是为了防止 Firefox 的多个实例运行,但我不知道如何在 Python 中做到这一点。

4

3 回答 3

2

有几种常见的方法可以执行您的第 1 项(检测正在运行的进程),但要使用它们,首先需要您稍微调整一下在第一次调用程序时如何启动这些后台进程的心理画面。

认为第一个程序不是启动五个进程然后退出,而是检测它是第一个启动的实例而不是退出。它可以创建文件锁(防止应用程序多次出现运行的常用方法之一),或仅绑定到某个套接字(另一种常用方法)。任何一种方法都会在第二个实例中引发异常,然后它知道它不是第一个实例,并且可以将注意力重新集中在联系第一个实例上。

如果您正在使用multiprocessing,您应该能够简单地使用Manager支持,这涉及绑定到套接字以充当服务器。

第一个程序启动进程,创建队列、代理或其他。它创建一个管理器来允许访问它们,可能允许远程访问

随后的调用首先尝试在预定义的套接字上联系所述服务器/管理器(或使用其他技术来发现它所在的套接字)。server_forever()他们没有打电话,而是connect()使用通常的multiprocessing机制进行通信。

于 2010-01-09T02:16:40.170 回答
1

You need a client-server model on a local system. You could do this using TCP/IP sockets to communicate between your clients and servers, but it's faster to use local named pipes if you don't have the need to communicate over a network.

The basic requirements for you if I understood correctly are these:
1. A producer should be able to spawn consumers if none exist already.
2. A producer should be able to communicate with consumers.
3. A producer should be able to find pre-existing consumers and communicate with them.
4. Even if a producer completes, consumers should continue running.
5. More than one producer should be able to communicate with the consumers.

Let's tackle each one of these one by one:

(1) is a simple process-creation problem, except that consumer (child) processes should continue running, even if the producer (parent) exits. See (4) below.

(2) A producer can communicate with consumers using named pipes. See os.mkfifo() and unix man page of mkfifo() to create named pipes.

(3) You need to create named pipes from the consumer processes in a well known path, when they start running. The producer can find out if any consumers are running by looking for this well-known pipe(s) in the same location. If the pipe(s) do not exist, no consumers are running, and the producers can spawn these.

(4) You'll need to use os.setuid() for this, and make the consumer processes act like a daemon. See unix man page of setsid().

(5) This one is tricky. Multiple producers can communicate with the consumers using the same named pipe, but you cannot transfer more than "PIPE_BUF" amount of data from the producer to the consumer, if you want to reliably identify which producer sent the data, or if you want to prevent some kind of interleaving of data from different producers.

A better way to do (5) is to have the consumers open a "control" named pipe (/tmp/control.3456, 3456 being the consumer pid) on execution. Producers first set up a communication channel using the "control" pipe. When a producer connects, it sends its pid say "1234", to the consumer on the "control" pipe, which tells the consumer to create a named pipe for data exchange with the producer, say "/tmp/data.1234". Then the producer closes the "control" pipe, and opens "/tmp/data.1234" to communicate with the consumer. Each consumer can have its own "control" pipes (use the consumer pids to distinguish between pipes of different consumers), and each producer gets its own "data" pipe.. When a producer finishes, it should clean up its data pipe or tell the consumer to do so. Similarly, when the consumer finishes, it should clean up its control pipes.

A difficulty here is to prevent multiple producers from connecting to the control pipes of a single consumer at the same time. The "control" pipe here is a shared resource and you need to synchronize between different producers to access it. Use semaphores for it or file locking. See the posix_ipc python module for this.

Note: I have described most of the above in terms of general UNIX semantics, but all you really need is the ability to create daemon processes, ability to create "named" pipes/queues/whatever so that they can be found by an unrelated process, and ability to synchronize between unrelated processes. You can use any python module which provides such semantics.

于 2010-01-09T03:50:15.563 回答
1

看看这些不同的服务发现机制:http ://en.wikipedia.org/wiki/Service_discovery

基本思想是消费者在启动时将各自注册一个服务。生产者在启动时会经历发现过程。如果它找到消费者,它就会绑定到他们。如果它没有找到它们,它就会启动新的消费者。在大多数所有这些系统中,服务通常还可以发布属性,因此您可以让每个消费者唯一地标识自己并将其他信息提供给发现的生产者。

Bonjour/zeroconf 得到很好的跨平台支持。您甚至可以将 Safari 配置为向您显示本地网络上的 zeroconf 服务,这样您就可以使用它来为消费者调试服务广告。这种方法的一个优点是您可以轻松地在与消费者不同的机器上运行生产者。

于 2010-01-08T23:53:12.313 回答