【问题标题】:std::thread access to a function loaded from a shared librarystd::thread 访问从共享库加载的函数
【发布时间】:2015-05-08 16:06:39
【问题描述】:

在 Ubuntu 上,我有一个共享库 mylibrary.so,带有一个函数 AlphaFunction。我想使用dlopen 在 C++ 中加载这个函数,然后在两个不同的线程中调用它。但是,这给了我运行时错误,大概是因为两个线程都试图访问存储函数的同一内存。

库本身通过 USB 控制机械臂,我得到的实际运行时错误是:LIBUSB_ERROR_NO_DEVICE returned by the Write operation.

我知道如何使用std::atomic 来处理共享变量,但是共享函数呢?

例如:

void Foo(int (*FooFunction)())
{
    while(true)
    {
        FooFunction();
    }
}

void Bar(int (*BarFunction)())
{
    while(true)
    {
        BarFunction();
    }
}


int main()
{
    void* api_handle = dlopen("mylibrary.so", RTLD_NOW|RTLD_GLOBAL);
    int (*MoveRobot)() = (int (*)()) dlsym(api_handle, "Move");

    std::thread t1(Foo, MoveRobot);
    std::thread t2(Bar, MoveRobot);

    t1.join();
    t2.join();

    return 0;
}

【问题讨论】:

  • 你没有用extern "C"定义MoveRobot。如果您使用的是 C++ 编译器,则它的名称已被修改。见tldp.org/HOWTO/C++-dlopen/thesolution.html#externC
  • 可能无法正常工作,具体取决于 MoveRobot 的内部结构,所以我不会将其作为答案,但您是否尝试通过将调用包装到 FooFunction 和 @987654332 来限制对函数的访问@与std::mutex?
  • @jnbbender 谁在乎MoveRobot,它是main() 的自动变量。我想你的意思是Movemylibrary.so 库中。
  • @WhozCraig 我的道歉
  • 这不是因为访问了“存储函数的同一内存”,而是因为它们的 IO 函数不是为并发执行而设计的。或者可能两个调用者都没有正确设置我们看不到的参数,因为这不是实际代码。无论如何,通过 dynaload 同时调用相同的 function 与通过本地定义并没有什么不同。调用者和/或被调用者负责管理并发invoking函数。如果他们不这样做也不支持它,你必须....

标签: c++ multithreading shared-libraries dlopen


【解决方案1】:

我看过 cmets。这是一个涵盖所有问题的解决方案:

  • 机器人库不是线程安全的,并且
  • 对机器人库的所有调用都必须在同一个线程上

这个答案提出了一个解决方案,其中启动了第三个线程,该线程充当机器人请求编组器。其他线程将任务发布到该线程的队列,一次执行一个,调用结果通过调用者可以等待的未来返回。

#include <thread>
#include <mutex>
#include <queue>
#include <future>
#include <functional>

// these definitions here just to make the example compile
#define RTLD_NOW 1
#define RTLD_GLOBAL 2
extern "C" void* dlopen(const char*, int);
extern "C" void* dlsym(void*, const char*);


struct RobotCaller final
{
    RobotCaller()
    {
        _library_handle = dlopen("mylibrary.so", RTLD_NOW|RTLD_GLOBAL);
        _Move = (int (*)()) dlsym(_library_handle, "Move");

        // caution - thread starts. do not derive from this class
        start();
    }

    void start()
    {
        _robot_thread = std::thread([this]{
            consume_queue();
        });
    }

    ~RobotCaller() {
        if (_robot_thread.joinable()) {
            std::unique_lock<std::mutex> lock(_queue_mutex);
            _should_quit = true;
            lock.unlock();
            _queue_condition.notify_all();
            _robot_thread.join();
        }

        // close library code goes here
    }

    std::future<int> Move()
    {
        return queue_task(_Move);
    }

private:
    void consume_queue() {
        ;
        for(std::unique_lock<std::mutex> lock(_queue_mutex) ; !_should_quit ; lock.lock()) {
            _queue_condition.wait(lock, [this]{
                return _should_quit || (!_task_queue.empty());
            });

            if (!_task_queue.empty()) {
                auto task = std::move(_task_queue.front());
                _task_queue.pop();
                lock.unlock();
                task();
            }
        }
    }

    std::future<int> queue_task(int (*f)())
    {
        std::packaged_task<int()> task(f);
        auto fut = task.get_future();
        std::unique_lock<std::mutex> lock(_queue_mutex);
        _task_queue.push(std::move(task));
        return fut;
    }

private:
    // library management
    void* _library_handle = nullptr;
    int (*_Move)() = nullptr;

    // queue management
    std::thread _robot_thread;
    std::queue<std::packaged_task<int()>> _task_queue;
    bool _should_quit = false;
    std::mutex _queue_mutex;
    std::condition_variable _queue_condition;
};

void Foo(std::function<std::future<int>()> FooFunction)
{
    while(true)
    {
        // marshal the call onto the robot queue and wait for a result
        auto result = FooFunction().get();
    }
}

void Bar(std::function<std::future<int>()> BarFunction)
{
    while(true)
    {
        // marshal the call onto the robot queue and wait for a result
        auto result = BarFunction().get();
    }
}


int main()
{
    RobotCaller robot_caller;

    std::thread t1(Foo, std::bind(&RobotCaller::Move, &robot_caller));
    std::thread t2(Bar, std::bind(&RobotCaller::Move, &robot_caller));

    t1.join();
    t2.join();

    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-25
    • 1970-01-01
    • 1970-01-01
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 2015-05-05
    相关资源
    最近更新 更多