从 Python 访问 Matlab 时避免轮询



我想从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()函数。

你可以用另一种方式来解决这个问题,并使用文件系统作为在MATLAB和Python之间传递消息的一种方式。

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

现在在你的 python 代码中,使用一些可用的方法来监听文件系统中的更改,并通过指示一些变量来打破循环来响应。


编辑

以下是所提出解决方案的框架:

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 中给出的标准示例的简单端口。

或者,您可以将 PyQt 库与其 QFileSystemWatcher 类一起使用,而不是直接使用 Win32 API。

最新更新