【问题标题】:How do we await for C# async delegate function in C++/CX我们如何在 C++/CX 中等待 C# 异步委托函数
【发布时间】:2015-12-05 23:20:04
【问题描述】:

这是关于C++(不同平台的共享代码)与C#(Windows通用应用程序)之间的通信。我们知道下面是我们如何从C++C# 进行函数调用。

C#

class FooCS
{
    FooCS()
    {
        FooC c = new ref FooC(); 
        c.m_GetSomeValueEvent = GetSomeValueEvent;
        // Some other stuff ...
    }

    string GetSomeValueEvent()
    {
        // Some other stuff ... 
        return "Hello World"; 
    }
}

C++

public delegate Platform::String GetSomeValueEventHandler(); 

class FooC
{
    public:
    event GetSomeValueEventHandler^ m_GetSomeValueEvent; 
    void someFunction(){
        Platform::String^ str = m_GetSomeValueEvent();
        // Some other stuff ... 
    }
}

以上内容非常简单。但问题是C#中的这个字符串GetSomeValueEvent()做了一些繁重的任务,比如从数据库中读取数据,我必须把它变成async

    async Task<string> GetSomeValueEvent() {
        // Some other stuff ... 
        return "Hello World"; 
    }

现在问题来了:如何让我的C++ 代码等待这个委托函数的返回?换句话说,下面的签名应该修改成什么?

    public delegate Platform::String GetSomeValueEventHandler(); 

我一直在谷歌上搜索,找不到这个问题的正确术语。如果您确定这是不可能的,那么至少很高兴知道。


最终,这就是我们要做的:

    string GetSomeValueEvent() {
        // Convert a async method to non-async one
        var val = someAsyncMethod().Result; 
        // Some other stuff ... 
        return "Hello World"; 
    }

您必须确保GetSomeValueEvent 没有在主线程上运行以防止死锁。

【问题讨论】:

    标签: c# windows asynchronous c++-cx


    【解决方案1】:

    UPD:从事件中返回值是一个非常糟糕的主意。考虑其他选项,例如某种可观察模式,但只有一个观察者。

    正如@RaymondChen 提到的,对于异步事件,最好使用延迟模式。这是 Windows 运行时常用的模式。

    GetDeferral() 方法添加到您的事件参数中,该方法返回一个特殊的延迟对象。此对象必须具有Complete() 方法,该方法通知您的代码异步操作已完成。

    ================================================ ==============================

    async 关键字实际上不会改变函数原型。因此,您只需更改委托中的返回值以匹配原始值 (Task&lt;string&gt;)。

    但有一个问题:Windows 运行时没有 Task&lt;&gt; 类型,它是 TPL 的一部分,它是一个 .NET 库。相反,Windows 运行时使用IAsyncOperation&lt;&gt;IAsyncAction 来表示异步操作。

    所以这是你需要做的:

    1. 更改委托人的签名:

      public delegate Windows::Foundation::IAsyncOperation<Platform::String> GetSomeValueEventHandler();
      
    2. 使用AsAsyncOperation()扩展方法将Task&lt;&gt;转换为IAsyncOperation&lt;&gt;

      async Task<string> GetSomeValueEvent()
      {
          // Some other stuff ... 
          return "Hello World"; 
      }
      
      IAsyncOperation<string> GetSomeValueEventWrapper()
      {
          return GetSomeValueEvent().AsAsyncOperation();
      }
      
      FooCS()
      {
          FooC c = new ref FooC(); 
          c.m_GetSomeValueEvent = GetSomeValueEventWrapper;
          // Some other stuff ...
      }
      

    【讨论】:

    • 一旦您有两个事件订阅者,这就会崩溃,因为只有其中一个将用于返回值。该类将只等待一个订阅者任务完成。更好的是使用延迟模式。
    • @RaymondChen 我同意,我应该提到它。但是从事件处理程序返回一个值是一个非常糟糕的主意,老实说,我不知道如何使用延迟模式来解决这个问题。
    • Windows 运行时模式是传递一个EventArgs 参数,该参数具有GetDeferral 方法和一些报告结果的机制。每个委托都进行延迟,执行其异步工作,通过报告机制(可能是SetResult 方法,或通过将结果附加到集合)报告结果,然后完成延迟。当最后一次延迟完成时,调用者检查所有报告的结果。
    • 您好,感谢您的建议。不过,我采用了不同的解决方案。我同意拥有 async delegate 并更好地使用 Raymond 建议的延迟模式不是一个好主意。我结束了使用Result 从C# 中删除async。如果您有兴趣,请随时查看我修改后的问题(在底部)。
    【解决方案2】:

    我要做的是使用 c++ 等价物:

     var result = GetSomeValueEvent().GetAwaiter().GetResult();
    

    这将为您做的不仅是获取结果,而且还传递该任务中发生的所有异常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-06
      • 1970-01-01
      • 1970-01-01
      • 2020-07-10
      • 1970-01-01
      • 2012-05-29
      相关资源
      最近更新 更多