【发布时间】:2020-11-03 20:53:38
【问题描述】:
我正在为 Arinc429 设备设计一个 API 接口。不同的供应商在设备中提供不同的功能,所以我决定有一套必须为每个实体实施的通用和预期的方法。
特别是,我有一个关于输出通道的问题。它提供了单个输出字节数组的方法,调用方法后输出本身是异步的。其中一种设备实现了可以通过操作系统实用程序捕获的中断(Windows 中的WaitForSingleObject)。
所以在这种特殊情况下,我有一个实现IArinc429Device 的对象,它:
- 实现方法
CaptureInterrupt,在发生中断时调用特定通道的回调; - 持有一个运行
CaptureInterrupt的线程;
void ArincPCI429_3::CaptureInterrupt()
{
HANDLE hEvent = CreateEvent(nullptr, TRUE, FALSE, "PCI429_3Interrupt");
// register event handle within device via DeviceIoControl
while (true)
{
DWORD waitRes = WaitForSingleObject(hEvent, INFINITE);
ResetEvent(hEvent);
// get output channel index which has generated an interrupt
size_t channelIndex = ...;
// private implementation holds pointers to output channels
pImpl_->GetChannelOut(channelIndex)->InvokeInterrupCallback();
}
}
但是另一个设备没有实现中断,所以我必须“忙等待”(休眠计算的预期时间,然后循环休眠一小段时间以调整可能的不准确性)。
实现接口IArinc429ChannelOutput的对象:
- 实现方法
SingleOutput,启动异步输出; - 实现方法
WaitOutputFinished,等待通道运行,然后将其状态修改为stopped; - 持有一个运行
WaitOutputFinished的线程;
void ArincECE206_1ChannelOutput::WaitOutputFinished(size_t words)
{
// calculate expected period of time to sleep, using amount of words to transfer and channel output speed
std::chrono::microseconds timeToSleep = ...;
// 99% is enough
std::this_thread::sleep_for(timeToSleep);
// if channel is still running, wait for 1 more word.
timeToSleep = Arinc429Values::TimeToTransfer(
refImpl_.outputFreqs[ChannelIndex()],
1);
while(IsRunning())
{
std::this_thread::sleep_for(timeToSleep);
++additionalWords;
}
mode_ = Arinc429OutMode::stopped;
if (callbackOnFinishedOutput_)
callbackOnFinishedOutput_();
}
这是输出通道 API 的一部分
struct ARINC429_API IArinc429ChannelOutput
{
// 0 based index
virtual size_t ChannelIndex() const = 0;
virtual Arinc429OutMode OutputMode() const = 0;
virtual Arinc429Freq Frequency() const = 0;
virtual size_t BufferSize() const = 0;
virtual void SetFinishOutputCallback(std::function<void()>&& fCallBack) = 0;
// elements exceeding BufferSize are ignored
// TODO: return future?
virtual bool SingleOutput(array_view<const uint32_t> wordArray) = 0;
virtual void StopOutput() = 0;
virtual ~IArinc429ChannelOutput() = default;
};
鉴于输出的异步性质,我认为从SingleOutput 返回std::future 会很方便。对于第二种类型的 Arinc429 设备,我认为这样做没有问题,因为单独的通道对象拥有自己单独的等待线程。
我选择从一开始就在完成的输出上添加回调,因为中断是为第一个设备实现的。回调也可以方便地发出 Qt 信号。
但std::future 更便于同步,可用于等待输出完成。虽然使用回调和条件变量也可以管理它,但我觉得这种方法不太方便。
选择什么选项?
一个。定义注册和使用回调的例程。
b.将std::future 定义为SingleOutput 的返回类型。
c.定义两者。这合理甚至可能吗?这意味着调用std::promise<R>::set_value 而不是调用。
另一个问题是关于实现的。
对于实现中断的设备,我没有看到一种清晰而简单的方法来实现返回std::future,因为所有通道都有一个公共中断事件和捕获线程。
如何为都驻留在不同线程中的多个输出通道对象提供期货?见ArincPCI429_3::CaptureInterrupt()
【问题讨论】:
标签: c++ multithreading c++11 asynchronous future