C++ std::线程访问从共享库加载的函数

C++ std::线程访问从共享库加载的函数,c++,multithreading,shared-libraries,dlopen,C++,Multithreading,Shared Libraries,Dlopen,在Ubuntu上,我有一个共享库mylibrary.so,带有一个函数AlphaFunction。在C++中,使用 dLOpen/加载该函数,然后用两个不同的线程调用该函数。但是,这给了我运行时错误,可能是因为两个线程都试图访问存储函数的相同内存 库本身通过USB控制机器人手臂,我得到的实际运行时错误是:LIBUSB\u error\u没有写入操作返回的设备。 我知道如何使用std::atomic处理共享变量,但是共享函数呢 例如: void Foo(int (*FooFunction)())

在Ubuntu上,我有一个共享库
mylibrary.so
,带有一个函数
AlphaFunction
。在C++中,使用<代码> dLOpen/<代码>加载该函数,然后用两个不同的线程调用该函数。但是,这给了我运行时错误,可能是因为两个线程都试图访问存储函数的相同内存

库本身通过USB控制机器人手臂,我得到的实际运行时错误是:
LIBUSB\u error\u没有写入操作返回的设备。

我知道如何使用
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;
}

我看了一下评论。以下是一个涵盖所有问题的解决方案:

  • robot库不是线程安全的,并且
  • 对robot库的所有调用必须在同一线程上
这个答案提出了一个解决方案,其中启动第三个线程作为机器人请求封送器。其他线程将任务发送到此线程的队列,每次执行一个任务,调用结果通过调用方可以等待的未来返回

#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;
}
#包括
#包括
#包括
#包括
#包括
//这里的这些定义只是为了使示例能够编译
#现在定义RTLD_1
#定义RTLD_全局2
外部“C”void*dlopen(常量字符*,int);
外部“C”void*dlsym(void*,const char*);
结构机器人呼叫器最终
{
机器人呼叫器()
{
_library_handle=dlopen(“mylibrary.so”,RTLD_NOW | RTLD_GLOBAL);
_Move=(int(*)()dlsym(_library_handle,“Move”);
//注意-线程启动。不要从此类派生
start();
}
void start()
{
_robot_thread=std::thread([此]{
消费队列();
});
}
~RobotCaller(){
if(_robot_thread.joinable()){
std::唯一的锁(队列互斥);
_应该退出=真;
lock.unlock();
_队列条件。通知所有();
_robot_thread.join();
}
//关闭库代码在这里
}
std::future Move()
{
返回队列任务(移动);
}
私人:
无效消费队列(){
;
对于(std::unique_lock lock(_queue_mutex);!_应该退出;lock.lock()){
_队列条件。等待(锁定[此]{
return | | |(!_task_queue.empty());
});
if(!\u task\u queue.empty()){
自动任务=std::move(_task_queue.front());
_task_queue.pop();
lock.unlock();
任务();
}
}
}
std::未来队列任务(int(*f)()
{
std::打包任务(f);
auto fut=task.get_future();
std::唯一的锁(队列互斥);
_task_queue.push(std::move(task));
返回fut;
}
私人:
//图书馆管理
void*\u library\u handle=nullptr;
int(*u Move)()=nullptr;
//队列管理
标准::线程_机器人_线程;
std::queue\u task\u queue;
bool(应该)退出=false;
std::mutex\u queue\u mutex;
std::条件\变量\队列\条件;
};
void Foo(std::function Foo函数)
{
while(true)
{
//将调用封送到robot队列并等待结果
自动结果=FooFunction().get();
}
}
空栏(标准::函数栏函数)
{
while(true)
{
//将调用封送到robot队列并等待结果
自动结果=BarFunction().get();
}
}
int main()
{
机器人呼叫者机器人呼叫者;
std::线程t1(Foo,std::bind(&RobotCaller::Move,&robot_caller));
std::线程t2(Bar、std::bind(&RobotCaller::Move,&robot_caller));
t1.join();
t2.连接();
返回0;
}

您尚未使用
extern“C”
定义
MoveRobot
。如果你使用C++编译器,它已经被名字损坏了。See可能不起作用,这取决于
MoveRobot
的内部结构,因此我不想以此作为答案,但您是否尝试过通过将对
FooFunction
BarFunction
的调用包装为
std::mutex?
@JNBender谁关心
MoveRobot
,来限制对该函数的访问,这是一个自动变量iin
main()
。我想你的意思是
mylibrary中的
Move
。所以
library.@WhozCraig我道歉这不是因为访问了“存储函数的同一内存”,而是因为它们的IO函数不是为并发执行而设计的。或者两个调用者都没有正确的参数设置,我们无法看到,因为这不是实际的代码。无论如何,通过dynaload并发调用同一函数与通过本地定义调用同一函数没有什么不同。调用方和/或被调用方负责管理调用函数的并发性。如果他们不做,也不支持,你必须。。。。