Javascript 来自C+的节点FFI回调+;线
我今天遇到了一个令人沮丧的问题。我正在使用<代码>节点FFI<代码>来运行我的电子应用程序中的C++代码。总的来说,我有很好的经验,但我今天开始使用多线程,遇到了一些困难。我传入的Javascript 来自C+的节点FFI回调+;线,javascript,c++,multithreading,electron,node-ffi,Javascript,C++,Multithreading,Electron,Node Ffi,我今天遇到了一个令人沮丧的问题。我正在使用节点FFI来运行我的电子应用程序中的C++代码。总的来说,我有很好的经验,但我今天开始使用多线程,遇到了一些困难。我传入的ffi回调是从线程调用的。但是,当我结束循环并尝试将循环线程加入主线程时,它会完全冻结electron应用程序 完全免责声明:我对C++非常陌生,并且会感谢我的代码中的任何反馈来改进它,特别是你认为我应该注意的任何红旗。 下面是两份回购协议,它们证明了我遇到的错误: 电子项目- C++ DLL—< /P> 下面是我所做工作的概述: 在
ffi
回调是从线程调用的。但是,当我结束循环并尝试将循环线程加入主线程时,它会完全冻结electron应用程序
完全免责声明:我对C++非常陌生,并且会感谢我的代码中的任何反馈来改进它,特别是你认为我应该注意的任何红旗。
下面是两份回购协议,它们证明了我遇到的错误:
电子项目-
C++ DLL—< /P>
下面是我所做工作的概述:
在我的dll中,我公开了开始/结束会话和开始/停止流的函数。这些调用类实例的引用来实际实现功能。本质上,它是一个C封装,围绕更强大的C++类。
// ThreadedDll.h
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifdef THREADEDDLL_EXPORTS
#define THREADEDDLL_API __declspec(dllexport)
#else
#define THREADEDDLL_API __declspec(dllimport)
#endif
THREADEDDLL_API void beginSession(void(*frameReadyCB)());
THREADEDDLL_API void endSession();
THREADEDDLL_API void startStreaming();
THREADEDDLL_API void stopStreaming();
#ifdef __cplusplus
}
#endif
// ThreadedDll.cpp
#include "ThreadedDll.h"
#include "Threader.h"
static Threader *threader = NULL;
void beginSession(void(*frameReadyCB)())
{
threader = new Threader(frameReadyCB);
}
void endSession()
{
delete threader;
threader = NULL;
}
void startStreaming()
{
if (threader) threader->start();
}
void stopStreaming()
{
if (threader) threader->stop();
}
下面是Threader
类的外观:
// Threader.h
#pragma once
#include <thread>
#include <atomic>
using std::thread;
using std::atomic;
class Threader
{
public:
Threader(void(*frameReadyCB)());
~Threader();
void start();
void stop();
private:
void renderLoop();
atomic<bool> isThreading;
void(*frameReadyCB)();
thread myThread;
};
// Threader.cpp
#include "Threader.h"
Threader::Threader(void(*frameReadyCB)()) :
isThreading{ false },
frameReadyCB{ frameReadyCB }
{
}
Threader::~Threader()
{
if (myThread.joinable()) myThread.join();
}
void Threader::start()
{
isThreading = true;
myThread = thread(&Threader::renderLoop, this);
}
void Threader::stop()
{
isThreading = false;
if (myThread.joinable()) myThread.join();
}
void Threader::renderLoop()
{
while (isThreading) {
frameReadyCB();
}
}
在app.js中运行的是主电子进程。我希望看到
start stream
Frame Ready (3800)
stop stream
end session
但它不显示结束会话
。但是,如果我在C++中删除了行<代码> FrAffeEdyBy](/Cuff>),它就如预期的那样工作。因此,ffi回调引用在某种程度上破坏了多线程环境。我很想听听你的想法。谢谢 问题
您的应用程序已死锁。在您的示例中,有两个线程:
$npm start
时创建,以及Threader::start()
中创建frameReadyCB()
,它将阻塞线程,直到线程完成。显示回调将在线程1上执行
不幸的是,线程1已经在忙于第二次设置超时,正在调用stopStreaming()
<代码>线程器::停止尝试加入线程2,直到线程2完成为止
你现在陷入僵局了。线程2正在等待线程1执行回调,线程1正在等待线程2完成执行。他们俩都在互相等候
通过节点ffi解决方案
当使用async()
通过节点ffi创建线程时,节点ffi似乎会处理在单独线程上运行的回调。因此,您可以从C++库中删除线程,而从节点库调用<代码> DLLIB .SistPosiv.AsiNC(()){“}”/代码>。
C++解决方案
为了解决这个问题,您需要确保在线程2等待frameReadyCB()
完成时,您永远不会尝试加入线程2。您可以使用互斥锁执行此操作。另外,当线程2等待frameReadyCB()
时,您需要确保不要等待锁定互斥锁。唯一的方法是创建另一个线程来停止流。下面的例子是使用节点FFI <代码> Aycy,虽然它可以在C++库内完成,以将其隐藏在节点库中。
//Threader.h
#布拉格语一次
#包括
#包括
使用std::线程;
使用std::原子;
使用std::mutex;
类螺纹机
{
公众:
螺纹机(void(*frameReadyCB)();
~Threader();
void start();
无效停止();
私人:
void renderLoop();
原子线程;
无效(*frameReadyCB)();
线程读取;
互斥mtx;
};
//Threader.cpp
#包括“Threader.h”
线程器::线程器(void(*frameReadyCB)():
isThreading{false},
frameReadyCB{frameReadyCB}
{
}
Threader::~Threader()
{
停止();
}
void Threader::start()
{
isThreading=true;
myThread=thread(&Threader::renderLoop,this);
}
void Threader::stop()
{
isThreading=false;
mtx.lock();
if(myThread.joinable())myThread.join();
mtx.unlock();
}
void Threader::renderLoop()
{
while(isThreading){
mtx.lock();
frameReadyCB();
mtx.unlock();
}
}
//threadeddl.js
常数ffi=要求(“ffi”);
const path=require('path');
const DllPath=path.resolve(_dirname,'../dll/threadeddl.dll');
//以FFI期望的方式映射库函数
常数DllMap={
'beginSession':['void',['pointer']],
'结束会话':['无效',[]],
“startStreaming”:[“void”,[],
“stopStreaming”:[“void”,[],
};
//使用ffi、DLL和函数表创建库
常量DllLib=ffi.Library(DllPath,DllMap);
类threadedll{
构造函数(args){
this.frameReadyCB=ffi.Callback('void',[],()=>{
console.log('Frame Ready');
});
DllLib.beginSession(this.frameReadyCB);
}
startStreaming(){
DllLib.startStreaming();
}
停止流式处理(){
DllLib.stopStreaming.async(()=>{});
}
(完){
DllLib.endSession.async(()=>{});
}
}
module.exports=threadeddl;
start stream
Frame Ready (3800)
stop stream
end session