异步P/Invoke调用

10

我正在为一个机器人控制器开发包装库,其中大部分依赖于 P/Invoke 调用。

然而,机器人的很多功能,例如归位或移动,需要相当长的时间,并且在运行时会进行线程锁定。

因此,我想知道如何以异步方式包装功能,以便调用不会阻塞我的 UI 线程。 我目前的想法是使用任务,但我不确定这是否是正确的方法。

public Task<bool> HomeAsync(Axis axis, CancellationToken token)
{
    return Task.Factory.StartNew(() => Home(axis), token);
}

目前,大多数关于.NET中异步模型的MSDN文章主要依赖已经具有异步功能的库(例如File.BeginRead等)来传递信息。但我似乎找不到有关如何编写异步功能的详细信息。


你正在使用哪种接口?还是这是一个必须独立于特定接口构建的库? - Pieter van Ginkel
假设您指的是UI界面,那么构建在其上的应用程序是WPF。但我更喜欢在库级别上处理它,这样UI团队就不必过多担心线程问题。 - Claus Jørgensen
1
机器人接口通常不会以这种方式工作。您将完全失去对设备的控制,甚至无法中止回原点移动。回原点命令通常只是启动回原点移动,然后您需要轮询或获取信号以确认其完成。在采用此方法之前,请检查接口中提供的选项。 - Hans Passant
当完成时,我没有得到任何信号。它是大学里的一件古老设备,可以追溯到90年代初。我对所需的东西非常有把握。 - Claus Jørgensen
我有类似的问题。你的最终实现是什么? - rollsch
显示剩余2条评论
3个回答

6
在进行了一些充分的讨论后,我认为这样的方式将会是一个很好的折中方案。
public void HomeAsync(Axis axis, Action<bool> callback)
{
    Task.Factory
        .StartNew(() => Home(axis))
        .ContinueWith(task => callback(task.Result));
}

我认为这是兼顾两种优势的做法。


5

你是否尝试过使用async delegates?我相信没有比它更简单的了。

如果你的线程阻塞方法是void Home(Axis),那么你首先需要定义一个委托类型,即delegate void HomeDelegate(Axis ax),然后使用一个新的HomeDelegate实例的BeginInvoke方法指向Home方法的地址。

[DllImport[...]] //PInvoke
void Home(Axis x);

delegate void HomeDelegate(Axis x);

void main()
{
    HomeDelegate d = new HomeDelegate(Home);
    IAsyncResult ia = d.BeginInvoke(axis, null, null);
    [...]
    d.EndInvoke(ia);
}

请记住,在某个地方使用EndInvoke(阻塞线程直到方法最终结束,可能与IAsyncResult.Completed属性的轮询一起使用)非常有用,以检查您的异步任务是否已真正完成。您可能不希望您的机器人在杯子还没摆放好的情况下就伸出手臂离开,您懂的;-)

2
而且你甚至不需要再定义委托类型了。只需使用 Action<Axis> 即可。 - erikkallen
我相信任务并行库将所有内容都封装到了Task类中,是吗? - Claus Jørgensen

1

我的第一反应是这不是你应该在库中做的事情。主要原因是你实现这样一个系统的方式可能取决于你正在构建的库的接口类型。

话虽如此,你基本上有两个选择。第一个是 IAsyncResult。可以在 http://ondotnet.com/pub/a/dotnet/2003/02/24/asyncdelegates.html 找到对此的良好描述。

第二个选项是创建带有回调事件的命令。用户创建一个命令类并在其中设置回调。然后,你将命令安排到 ThreadPool 中,在命令执行完毕后,你会触发该事件。

.NET 框架的旧接口主要采用了 IAsyncResult 方法,而新接口则倾向于采用事件回调方法。


将项目排队到线程池中不会阻塞UI线程吗?即使我有事件告诉我操作何时完成,如果我仍然需要将所有调用包装到线程中以避免UI阻塞,那么这是相当无意义的。 - Claus Jørgensen
将其排队到“线程池”不会阻塞。使用回调接口的整个原因是您可以将同步留给UI界面实现。您只需提供一种向UI发出完成信号的方法,然后UI就会接管。另一个优点是回调是可选的,因此当UI不需要通知时,它只需不注册回调并快速执行即可。 - Pieter van Ginkel
1
除了IAsyncResult模型需要大量不必要的样板代码外。TPL不仅适用于并行代码,还适用于异步操作。Hejlsberg甚至在昨天的PDC演讲中确认了这一点。 - Claus Jørgensen
当然可以,但是添加回调函数并不会自动使代码异步运行,并且也不能阻止UI线程的阻塞。 - Claus Jørgensen
想法是 ThreadPool 执行命令(包括 P/Invoke),并在 ThreadPool 的线程中引发回调事件。 - Pieter van Ginkel
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接