如何在多线程 MEX 函数中打印到控制台?

How do you print to console in a Multi-Threaded MEX Function?

我正在编写一个使用 Boost 库的简单生产者消费者 MEX 函数。我已经设法使以下程序正常运行。

#include "mex.h"
#include <boost/thread/thread.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <iostream>
#include <boost/atomic.hpp>

int producer_count = 0;
boost::atomic_int consumer_count (0);
boost::lockfree::spsc_queue<int, boost::lockfree::capacity<1024> > spsc_queue;
const int iterations = 10000000;

void producer()
{
    for (int i = 0; i != iterations; ++i) {
        int value = ++producer_count;
        while (!spsc_queue.push(value));
    }
}

boost::atomic<bool> done (false);

void consumer()
{
    int value;
    while (!done) {
        while (spsc_queue.pop(value))
            ++consumer_count;
    }

    while (spsc_queue.pop(value))
        ++consumer_count;
}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    if (!spsc_queue.is_lock_free())
    {
      mexPrintf("boost::lockfree::queue is not lockfree\n");
      mexEvalString("drawnow;");
    }
    else
    {
      mexPrintf("boost::lockfree::queue is lockfree\n");
      mexEvalString("drawnow;");
    }

    boost::thread producer_thread(producer);
    boost::thread consumer_thread(consumer);

    producer_thread.join();
    done = true;
    consumer_thread.join();

    cout << "produced " << producer_count << " objects." << endl;
    cout << "consumed " << consumer_count << " objects." << endl;
}

最大的问题是我尝试将 mexPrintf() 包含到生产者或消费者方法中,MATLAB 就崩溃了。在做了一些调查后,我发现 this post 这解释了这是由于竞争条件而发生的。有谁知道我该如何解决这个问题?我读了关于 Mutex 的答案,但我不明白我将如何实现这样的功能。

您不能从主线程以外的任何线程调用 mexPrintf。互斥量不能解决您的问题。

来自MATLAB documentation

MEX API Is Not Thread Safe

Do not call a single session of MATLAB® on separate threads from a MEX file. The MEX and Matrix Library APIs are not multi-threaded.

You can create threads from a C MEX file; however, accessing MATLAB from those threads is not supported. Do not call any MEX API functions from the spawned threads, including printf, which is defined as mexPrintf in the mex.h header file.

如果您确实需要从这些线程生成输出,请考虑实现一个简单的消息传递系统,其中线程 post 一条包含要输出的文本的消息和主线程,而不是等待 producer_thread.join();,循环查找要打印的消息,并使用 mexPrintf.

打印它们

下面的代码未测试。它甚至没有被编译。将其视为伪代码。我认为这是对解决方案的合理尝试,但可能有更好的方法。继续风险自负。 :)

boost::lockfree::queue<std::string> message_queue;

void producer() {
    //...
    message_queue.push("A string to print!");
    //...
}

void mexFunction( /*...*/ ) {
    // ...
    boost::thread producer_thread(producer);
    boost::thread consumer_thread(consumer);
    while(producer_thread.joinable()) {
        join_for(boost::chrono::milliseconds(50));
        std::string s;
        while (message_queue.pop(s)) {
            mexPrintf("%s\n", s.c_str());
        }
    }
    producer_thread.join();
    done = true;
    consumer_thread.join();
    // ...
}

可以使用新的 C++ mex interface。在这里您可以使用 feval 或 fevalAsync 调用 matlab 函数。 fevalAsync 是从另一个线程调用 matlab 时的方法。

打印命令 window 的函数如下所示:

 void print(const std::string& msg)
 {
    mattlab::data::ArrayFactory factory;
    matlabPtr->fevalAsync(u"fprintf", 0,
        std::vector<matlab::data::Array>({ factory.createScalar(msg) }));
 });

但问题是消息是在 mex 函数完成后显示的。还有后续调用,例如

matlabPtr->evalAsync(u"pause(0.001);");

没有效果。如果有人有想法,我也将不胜感激。