【问题标题】:How to call and handle an Asynchronous F# Workflow from C#如何从 C# 调用和处理异步 F# 工作流
【发布时间】:2011-04-15 13:17:22
【问题描述】:

我阅读了几篇 F# 教程,并且注意到与 C# 相比,在 F# 中执行异步和并行编程是多么容易。因此,我正在尝试编写一个 F# 库,该库将从 C# 调用,并将 C# 函数(委托)作为参数并异步运行它。

到目前为止,我已经成功地传递了该函数(我什至可以取消),但我想念的是如何实现回调到 C#,一旦异步操作完成,它将立即执行它。 (例如,函数 AsynchronousTaskCompleted?)。我还想知道我是否可以从函数 AsynchronousTask 将(例如 Progress %)发回 F#。

有人可以帮帮我吗?

这是我目前写的代码(我对 F# 不熟悉,所以下面的代码可能是错误的或实现不佳)。

//C# Code Implementation (How I make the calls/handling)
        //Action definition is: public delegate void Action();
        Action action = new Action(AsynchronousTask);
        Action cancelAction = new Action(AsynchronousTaskCancelled);
        myAsyncUtility.StartTask2(action, cancelAction);
        Debug.WriteLine("0. The task is in progress and current thread is not blocked");
        ......
        private void AsynchronousTask()
        {
            //Perform a time-consuming task
            Debug.WriteLine("1. Asynchronous task has started.");
            System.Threading.Thread.Sleep(7000);
            //Post progress back to F# progress window?
            System.Threading.Thread.Sleep(2000);
        }        
        private void AsynchronousTaskCompleted(IAsyncResult asyncResult)
        {           
            Debug.WriteLine("2. The Asynchronous task has been completed - Event Raised");
        }
        private void AsynchronousTaskCancelled()
        {
            Debug.WriteLine("3. The Asynchronous task has been cancelled - Event Raised");
        }

//F# Code Implementation
  member x.StartTask2(action:Action, cancelAction:Action) = 
        async {
            do! Async.FromBeginEnd(action.BeginInvoke, action.EndInvoke, cancelAction.Invoke)
            }|> Async.StartImmediate
        do printfn "This code should run before the asynchronous operation is completed"    
        let progressWindow = new TaskProgressWindow()
        progressWindow.Run() //This class(type in F#) shows a dialog with a cancel button
        //When the cancel button is pressed I call Async.CancelDefaultToken()

  member x.Cancel() =
        Async.CancelDefaultToken()

【问题讨论】:

    标签: c# asynchronous f# callback workflow


    【解决方案1】:

    要获得 F# 异步工作流的好处,您必须在 F# 中实际编写异步计算。您尝试编写的代码将不起作用(即它可能会运行,但不会有用)。

    在 F# 中编写异步计算时,可以使用 let!do! 进行异步调用。这允许您使用其他原始非阻塞计算。例如,您可以使用Async.Sleep 代替Thread.Sleep

    // This is a synchronous call that will block thread for 1 sec
    async { do Thread.Sleep(1000) 
            someMoreStuff() }
    
    // This is an asynchronous call that will not block threads - it will create 
    // a timer and when the timer elapses, it will call 'someMoreStuff' 
    async { do! Async.Sleep(1000)
            someMoreStuff() }
    

    您只能在 async 块内使用异步操作,它依赖于 F# 编译器处理 do!let! 的方式。对于以顺序方式编写的代码(例如,在 C# 中或在 F# 中的 async 块之外),没有(简单的)方法可以获得真正的非阻塞执行。

    如果你想使用 F# 来获得异步工作流的好处,那么最好的选择是在 F# 中实现这些操作,然后使用 Async.StartAsTask 将它们暴露给 C#(它为你提供了 C# 可以轻松使用的 Task<T> )。像这样的:

    let someFunction(n) = async {
      do! Async.Sleep(n)
      Console.WriteLine("working")
      do! Async.Sleep(n)
      Console.WriteLine("done") 
      return 10 }
    
    type AsyncStuff() = 
      member x.Foo(n) = someFunction(n) |> Async.StartAsTask
    
    // In C#, you can write:
    var as = new AsyncStuff()
    as.Foo(1000).ContinueWith(op =>
        // 'Value' will throw if there was an exception
        Console.WriteLine(op.Value))
    

    如果您不想使用 F#(至少用于实现异步计算),那么异步工作流将无济于事。您可以使用TaskBackgroundWorker 或其他 C# 技术实现类似的东西(但您将失去在不阻塞线程的情况下轻松运行操作的能力)。

    【讨论】:

    • 代码似乎工作正常,但我不确定这是否应该实现。如果我在 F# 中编写异步计算,我可以在 C# 中使用它吗?
    • 感谢您的回复,我会尝试熟悉异步计算。
    • @loannis:我添加了一些信息和示例,您可以如何在 F# 中编写异步工作流并使用 StartAsTask 在 C# 中使用它。希望这会有所帮助!
    • 非常感谢您对我的帮助。
    • 感谢您的提问和回答! @Tomas,您是否知道是否有某种方法可以在 C# 中使用 F# 的 Async 类型,或者您通常会建议使您的 F# 代码对 C# 友好,而不是相反?
    猜你喜欢
    • 2015-11-18
    • 1970-01-01
    • 2014-04-10
    • 1970-01-01
    • 2014-03-26
    • 1970-01-01
    • 1970-01-01
    • 2018-09-21
    • 1970-01-01
    相关资源
    最近更新 更多