从 Python 访问 Matlab 时逃避轮询

Posted

技术标签:

【中文标题】从 Python 访问 Matlab 时逃避轮询【英文标题】:Evade polling when accessing Matlab from Python 【发布时间】:2012-06-04 14:05:24 【问题描述】:

我想从 Python 访问 Matlab(在 Windows 上,远程和通过 COM 接口)。我的目标是:Matlab 正在做一些工作并永久更改某个变量的值。我需要知道该值何时超过某个常数。现在,我正在无限循环中轮询 Matlab 中该变量的值,该循环会在超过该值时中断。但是,我想让 Matlab 完成这项工作并告诉 什么时候是这种情况,而我却懒洋洋地坐在那里听。有没有办法做到这一点,如何做到最好?我曾想过定义一个要传递给 Matlab 的回调函数,它在超出事件时会触发 Python 中非忙等待循环的中断,但我怀疑它会起作用。我在 Matlab 和 Python 方面都不是很有经验,所以非常感谢提示。

还有很多其他的代码,但基本上现在是这样的

connectToMatlab(*args)
while True:
    val = getValueFromMatlab()
    if val > constant or timeout: break

我想到的是

def breakLoop():
    ...  

connectToMatlab(breakLoop, *args)
while True:
    time.sleep(1) # or some alternate non-busy-wait

然后让 Matlab 在 val > constant 上调用 breakLoop()。但是,我不知道是否可以让 Matlab 通过回调来做到这一点,如果可以,如何实现这样的breakLoop()-Function。

【问题讨论】:

请提供您可以使用的代码来显示您尝试过的内容。 不幸的是,这不是真正的 my 代码,我想避免发布它的麻烦。我只是应该找到一个可能的解决方案,并希望得到一个指针。 如果您甚至可以发布一些sn-ps,那将有很大帮助。像对 python 的调用等。sn-ps 很少有问题。另外,如果您可以创建自己的独立简单代码库,它会更有帮助,而且在编写此类代码的过程中,我已经回答了不止一个问题。 【参考方案1】:

您可以采用另一种方式,并使用文件系统作为在 MATLAB 和 Python 之间传递消息的一种方式。

在您的 MATLAB 代码中,每次更改变量时,检查它是否超过某个阈值。如果是,请在预定位置创建一个新文件。将此视为触发事件。

现在在您的 python 代码中,使用文件系统中listen 到changes 的一些可用方法,并通过指示一些变量来中断循环来响应。


编辑

这里是提出的解决方案的框架:

matlab_script.m

%# directory that Python code is watching for modifications
dirPath = 'some_directory';

x = 0;
for i=1:1000
    %# some lengthy operation
    pause(0.5)
    x = x + 1;

    %# check if variable exceeds threshold
    if x > 10
        %# save the workspace to MAT-file inside the directory watched.
        %# this shall trigger the notification in Python
        save( fullfile(dirPath,'out.mat') )
        break
    end
end

python_code.py

import os, sys, time
import win32file, win32event, win32con

# stub your functions in my case
def connectToMatlab():
  pass
def getValueFromMatlab():
  return 99

# path to predetermined directory to watch
dirPath = "some_directory"
dirPath = os.path.abspath(dirPath)

# start/connect to a MATLAB session, running the script above
connectToMatlab()

# set up folder watching (notify on file addition/deletion/renaming)
print "Started watching '%s' at %s" % (dirPath, time.asctime())
change_handle = win32file.FindFirstChangeNotification(
  dirPath, 0, win32con.FILE_NOTIFY_CHANGE_FILE_NAME)

# time-out in 10 sec (win32event.INFINITE to wait indefinitely)
timeout = 10000

try:
  # block/wait for notification
  result = win32event.WaitForSingleObject(change_handle, timeout)

  # returned because of a change notification
  if result == win32con.WAIT_OBJECT_0:
    # retrieve final result from MATLAB
    print "MALTAB variable has exceeded threshold at %s" % time.asctime()
    val = getValueFromMatlab()

  # timed out
  elif result == win32con.WAIT_TIMEOUT:
    print "timed-out after %s msec at %s" % (timeout,time.asctime())
    val = None    # maybe to indicate failure

finally:
  # cleanup properly
  win32file.FindCloseChangeNotification(change_handle)

# work with val
print val

WaitForSingleObject 函数首先检查指定对象的状态。如果它是无信号的,则调用线程进入有效的等待状态,并在等待对象发出信号(或超时间隔过去)时消耗非常少的处理器时间。

当线程引用处于非信号状态的对象时,您会看到立即进行上下文切换,即从处理器中取出并进入等待/睡眠模式。稍后当对象发出信号时,线程被放回可运行队列并准备好执行。

在这种等待中,在等待状态下不会浪费 CPU 周期,尽管在上下文切换中有一些开销。

将此与“轮询并等待”方法进行比较,其中线程在某种循环中等待并检查感兴趣对象的状态。这称为自旋或忙等待,这可能会浪费 CPU 周期。

现在感谢 pywin32 模块,我们可以直接使用 WaitFor... 函数。实现应该是 MSDN 中给出的standard example 的直接端口。

或者,您可以使用带有 QFileSystemWatcher 类的 PyQt 库,而不是直接使用 Win32 API。

【讨论】:

感谢您的建议。不幸的是,msdn 上似乎没有提示WaitForSingleObject 本身是否进行了一种忙等待。无论如何 +1 为“将文件视为信使”-idea。 @AFriedrich:其实WaitFor*的函数效率很高,看我上面的解释。我还贴了一个示例实现 非常感谢!我会尝试这种方法,并在我将其纳入工作代码时留下通知(尽管可能需要一段时间)。

以上是关于从 Python 访问 Matlab 时逃避轮询的主要内容,如果未能解决你的问题,请参考以下文章

从 MATLAB Compiler 应用程序调用 python 时无法调用 python 库

在 python 中访问包含 matlab 类的 .mat 文件

在python中轮询键盘(检测按键)

从 MATLAB 调用 Python 函数

逃避检测硒自动化

从 Python 运行 Matlab 脚本时在 Python 中检测错误并引发标志