简体   繁体   English

将第三方API调用转换为异步

[英]Converting a 3rd party API call into Async

I'm reading left and right about Async and Task but I still don't get how one can wrap an existing 3rd party call into an async method. 我在左右阅读有关异步和任务的内容,但是我仍然不知道如何将现有的第三方呼叫包装到异步方法中。

Mainly the 3rd party dll offers a method to call one of their APIs 主要是第3方dll提供了一种调用其API之一的方法

var client = new FooClient();
var response = client.CallMethod(param1, message);

if (response.RestException != null)
{
    status = response.RestException.Message;
    return false;
}
else 
   return true

and this call as expected will block the current thread till it returns the response. 并且此调用将按预期方式阻塞当前线程,直到返回响应。

So I looked at Task and other ways to make this call Async but still get the response back cause based on that the application will take different actions. 因此,我研究了Task和其他使此调用异步的方法,但仍然基于应用程序将采取不同的操作而使响应返回原因。

I kind of feel Task is the way to go but their DLL does NOT have an CallMethodAsSync method so I don't know what to do at this point. 我觉得Task是可行的方法,但是他们的DLL没有CallMethodAsSync方法,因此我现在不知道该怎么办。

Any help will be much appreciated !! 任何帮助都感激不尽 !!

The simplest way to do this is to just dump the call into a Task.Run, and then await the return of the Task.Run. 最简单的方法是将调用转储到Task.Run,​​然后等待Task.Run返回。 This will offload the intensive work to another thread, allowing the UI thread to continue. 这会将繁重的工作转移到另一个线程,从而允许UI线程继续。 Here's a simple example of a method that has to wait for another time-intensive method before it can return: 这是一个方法的简单示例,该方法必须等待另一个耗时的方法才能返回:

    static void Main()
    {
        Console.WriteLine("Begin");
        var result = BlockingMethod("Hi!");
        Console.WriteLine("Result: " + result);
        Console.ReadLine();
    }

    static bool BlockingMethod(string someText)
    {
        Thread.Sleep(2000);
        return someText.Contains("SomeOtherText");
    }

As we can see, the BlockingMethod method has a Thread.Sleep(2000) as its first statement. 如我们所见,BlockingMethod方法的第一个语句具有Thread.Sleep(2000)。 This means that the thread running the calling method Main has to wait a full two seconds before it can get the result of BlockingMethod. 这意味着运行调用方法Main的线程必须等待整整两秒钟,才能获取BlockingMethod的结果。 If the thread running the Main method is dealing with UI repainting, then this means that we get a UI that appears unresponsive/locked for a full two seconds. 如果运行Main方法的线程正在处理UI重绘,那么这意味着我们得到的UI在整整两秒钟内都没有响应/锁定。 We can offload the work of waiting for BlockingMethod onto another thread like so: 我们可以将等待BlockingMethod的工作转移到另一个线程上,如下所示:

First we mark our calling method as 'async' because this tells the compiler to generate something resembling a state machine for all of the 'await's in the async method: 首先,我们将调用方法标记为“异步”,因为这会告诉编译器为异步方法中的所有“等待”生成类似于状态机的内容:

    static async void Main()
    {
        Console.WriteLine("Begin");
        var result = BlockingMethod("Hi!");
        Console.WriteLine("Result: " + result);
        Console.ReadLine();
    }

Next we turn the return type 'void' into Task. 接下来,将返回类型“ void”转换为Task。 This allows it to be awaited by other threads (you can leave it as async void if you don't care about that, but async methods with a return type need to return Task): 这使它可以被其他线程等待(如果您不关心它,可以将其保留为异步空,但是具有返回类型的异步方法需要返回Task):

    static async Task Main()
    {
        Console.WriteLine("Begin");
        var result = BlockingMethod("Hi!");
        Console.WriteLine("Result: " + result);
        Console.ReadLine();
    }

Now our method is allowed to run asynchronously, and it's allowed to be awaited by other methods. 现在,我们的方法被允许异步运行,并且其他方法也可以等待它。 But this hasn't solved our problem yet, because we're still synchronously waiting for the blocking method. 但这还没有解决我们的问题,因为我们仍在同步等待阻塞方法。 So we move our blocking method out to another thread by calling Task.Run, and awaiting its result: 因此,我们通过调用Task.Run并等待其结果将阻塞方法移至另一个线程:

    static async Task Main()
    {
        Console.WriteLine("Begin");
        await Task.Run(() => BlockingMethod("Hi!"));
        Console.WriteLine("Result: " + result);
        Console.ReadLine();
    }

But this presents us with a problem: our blocking method was returning something that we wanted to use later on in our method, but Task.Run returns void! 但这给我们带来了一个问题:我们的阻塞方法返回了我们稍后想在我们的方法中使用的东西,但是Task.Run返回void! In order to get access to the variable returned by the blocking method running in the other thread, we have to capture a local variable in a closure: 为了访问在另一个线程中运行的阻塞方法返回的变量,我们必须在闭包中捕获局部变量:

    static async Task Main()
    {
        Console.WriteLine("Begin");
        bool result = true; 
        await Task.Run(() => result = BlockingMethod("Hi!"));
        Console.WriteLine("Result: " + result);
        Console.ReadLine();
    }

So, in summary, what we did was we took a normal synchronous method (the calling method that we can rewrite, rather than the third party API that we can't rewrite) and we changed it so that it was marked as async and it returned a Task. 因此,总而言之,我们所做的是采用了普通的同步方法(可以重写的调用方法,而不是无法重写的第三方API),然后我们对其进行了更改,以使其标记为异步,并且返回了一个任务。 If we wanted it to return a result, then it'd need to be a generically typed Task. 如果我们希望它返回结果,则它必须是一个通用类型的Task。 After that, we wrapped the call to the blocking method in a Task.Run -- which creates a new thread for the blocking method to run on -- then we gave it an Action (in lambda syntax) to run. 之后,我们将对阻塞方法的调用包装在Task.Run中-为该阻塞方法创建一个新线程以在其上运行-然后为它提供一个Action(使用lambda语法)以运行。 In that Action, we referenced a variable defined in the calling function (this is the closure) which can capture the result of the blocking method. 在该Action中,我们引用了在调用函数(这是闭包)中定义的变量,该变量可以捕获阻塞方法的结果。

Now, we're asynchronously waiting for a synchronous, blocking method somewhere else. 现在,我们正在异步等待其他地方的同步阻塞方法。 It doesn't matter that the blocking method isn't inherently asynchronous, because we're letting it run synchronously in another thread, and allowing our thread to just await its result. 阻塞方法并不是固有异步的也没关系,因为我们让它在另一个线程中同步运行,并让我们的线程等待它的结果。

If any of this was unclear, then please comment. 如果不清楚,请发表评论。 Asynchrony is a bit confusing at first, but it's a godsend for responsive UIs. 异步一开始有点让人困惑,但是对于响应式UI来说是天赐之物。

EDIT: 编辑:

In response to a comment by Scott Chamberlain, Task.Run also has an overload which returns the type of the method its running (supplying it a Func, rather than an Action). 为了响应斯科特·张伯伦(Scott Chamberlain)的评论,Task.Run还具有一个重载,该重载返回其运行方法的类型(为它提供Func,而不是Action)。 So we can simply use: 所以我们可以简单地使用:

    static async Task MainAsync()
    {
        Console.WriteLine("Begin");
        bool result = await Task.Run(() => BlockingMethod("Hi!"));
        Console.WriteLine("Result: " + result);
        Console.ReadLine();
    }

Keep in mind -- as Scott Chamberlain points out -- that there's no inherent performance benefit to running work in another thread. 记住-正如Scott Chamberlain指出的-在另一个线程中运行工作并没有固有的性能优势。 In many cases, it may actually cause worse performance, as setting up and tearing down threads is expensive. 在许多情况下,由于设置和拆除线程非常昂贵,因此实际上可能会导致性能下降。 Task-based asynchrony is only useful to keep a busy thread (eg the UI thread) unblocked, so it can respond to requests, or when properly sub-dividing work through use of eg Parallel.ForEach. 基于任务的异步仅在保持繁忙的线程(例如UI线程)不受阻塞时有用,因此它可以响应请求,或者在通过使用例如Parallel.ForEach适当地细分工作时。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM