【问题标题】:Concurrency task never execute "then()" nor throws exception (C++/CX UWP)并发任务从不执行“then()”也不抛出异常 (C++/CX UWP)
【发布时间】:2018-01-11 10:37:12
【问题描述】:

我在使用 Concurrency::task 从我的代码中列出一些设备时遇到问题:

去年在 Visual Studio 2015 中使用此代码:

Concurrency::create_task(Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(Windows::Devices::Midi::MidiInPort::GetDeviceSelector()))
    .then([](Windows::Devices::Enumeration::DeviceInformationCollection ^midiDeviceCollection) {

        for (Windows::Devices::Enumeration::DeviceInformation^ portInformation : midiDeviceCollection){
            //do stuff with device
        }
    });

但几个月后,...今天,我必须更新我的应用程序(同时我切换到 VS2017)。所以我重新打开并更新了项目,遗憾的是这段代码不再按应有的方式工作。在“then()”部分设置的断点永远不会到达,代码会继续执行而不会出错,就好像我的任务从未被调用过一样。

我想知道在执行 DeviceInformation::FindAllAsync 时是否有错误导致“then()”部分无法运行。在对任务(https://msdn.microsoft.com/fr-fr/library/dd492427.aspx)进行了一些研究之后,我找到了一种在任务运行时捕获异常的方法,因此我将代码转换为:

    IAsyncOperation<DeviceInformationCollection^>^ deviceOp = DeviceInformation::FindAllAsync(MidiInPort::GetDeviceSelector());
    Concurrency::task<DeviceInformationCollection^> deviceEnumTask = Concurrency::create_task(deviceOp);

    deviceEnumTask.then([](Concurrency::task<DeviceInformationCollection^> t){

        try{
            DeviceInformationCollection ^midiDeviceCollection = t.get();

            for (DeviceInformation^ portInformation : midiDeviceCollection) {
                 //do stuff with device
            }

        }catch (Platform::Exception^ e){
            //do stuff with device
            OutputDebugString(e->Message->Data());
        }

    });

同样的结果,应用永远不会进入“then()”,也不会抛出异常:-( .

所以我的问题是:这里有什么问题?这段代码是列出设备的最常用方法,我在其他网络上的几个地方都看到过它(而且它在我的案例中去年有效),所以我应该犯了一个错误或在其他地方遗漏了一些东西。

这是捕获任务错误的好方法吗?

更多信息:此代码直接在 MainPage 构造函数中调用,就在 InitializeComponent(); 之后.我正在运行 VS2017,目标平台最低版本是 10.0.10240.0,目标平台版本是 10.0.15063.0。该应用是在 x64 桌面计算机上构建和测试的。

非常感谢您的帮助。

编辑:

感谢 Sunteen Wu 和 David Pritchard(见下文),我能够找出问题的根源。正如答案中所述,上面的代码实际上是有效的,并且在调整大卫提出的错误处理后非常清晰和完整。在我的测试中,我的断点也放在了正确的线上。但实际上,不知不觉中,我在“//do stuff with device”中使用了一些更深层次的阻塞代码(此处不可见)。这阻止了 MainPage 构造函数,避免它继续执行直到调用的任务完成......显然任务仍然与主线程执行相关联,只要 MainPage 构造没有结束就不会运行,在我的情况下从未发生过 - >死锁!

【问题讨论】:

    标签: exception concurrency uwp task c++-cx


    【解决方案1】:

    我无法回答您问题的 MIDI 部分。我可以和异常处理代码说话。

    首先 - 我怀疑您是否看到来自 FindAllAsync 的异常,因为未观察到的异常通常只会使应用程序崩溃(或中断调试器)。

    第二 - 上面的异常处理程序似乎混淆了两种类型的延续:

    • 类型 1: 常规延续,在异步方法返回其结果后继续工作。

      例如,deviceEnumTask.then([] (DeviceInformationCollection ^) { ... })

    • 类型2:异常处理延续,处理任务链中的任何异常。

      例如,deviceEnumTask.then([] (Concurrency::task&lt;void&gt; t) { ... })

    您的原始代码示例使用了类型 1,但没有包含类型 2,因此无法处理任何异常。

    您修改后的代码示例使用了非标准的延续(既不是类型 1 也不是类型 2),并且可能永远不会被调用。

    您想要的是同时使用 1 和 2,如下所示:

    using namespace Windows::Devices::Enumeration;
    using namespace Windows::Devices::Midi;
    Concurrency::create_task(
        DeviceInformation::FindAllAsync(MidiInPort::GetDeviceSelector())
    ).then([](DeviceInformationCollection ^midiDeviceCollection) {
        for (DeviceInformation^ portInformation : midiDeviceCollection){
            //do stuff with device
        }
    }).then([] Concurrency::task<void> t) {
       try {
          t.get();
          OutputDebugString(L"No exceptions seen");
       }
       catch (Platform::Exception ^e) {
          OutputDebugString(e->Message->Data());
       }
    });
    

    【讨论】:

    • 这个关于错误处理的解释很清楚,我错过了。
    【解决方案2】:

    同样的结果,应用永远不会进入“then()”并且没有抛出异常

    通过我这边的测试,在task::then里面设置断点时,它的delegate可以成功执行如下:

    应用确实进入了委托。如果在task::then 代码行设置断点并使用F10 跳过,它将跳过委托,因为task::then 方法立即返回,并且它的委托在异步工作成功完成之前不会运行.详情请参考Consuming an async operation by using a task

    这是捕获任务错误的好方法吗?

    根据Handling errors in a task chain,您尝试捕获错误的方式似乎是正确的。如果异步操作导致抛出异常,则延续永远不会执行。您将获得由task::get 传送到任务的任何异常。

    您还可以检查“不再工作”是否是由于没有MidiInPort 造成的。尝试获取如上图所示的设备尺寸。

    【讨论】:

      猜你喜欢
      • 2015-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-03
      • 1970-01-01
      • 2012-03-19
      相关资源
      最近更新 更多