gpt4 book ai didi

c# - 将第 3 方 API 调用转换为异步

转载 作者:太空狗 更新时间:2023-10-30 01:05:11 25 4
gpt4 key购买 nike

我正在左右阅读有关 Async 和 Task 的内容,但我仍然不明白如何将现有的第 3 方调用包装到异步方法中。

主要是第 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

这个调用如预期的那样将阻塞当前线程,直到它返回响应。

所以我查看了 Task 和其他方法来进行异步调用,但仍然得到响应,因为应用程序将采取不同的操作。

我觉得 Task 是可行的方法,但他们的 DLL 没有 CallMethodAsSync 方法,所以我现在不知道该怎么做。

任何帮助将不胜感激!!

最佳答案

执行此操作的最简单方法是将调用转储到 Task.Run 中,然后等待 Task.Run 的返回。这会将密集的工作卸载到另一个线程,允许 UI 线程继续。这是一个必须等​​待另一个耗时方法才能返回的方法的简单示例:

    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");
}

正如我们所见,BlockingMethod 方法的第一条语句是 Thread.Sleep(2000)。这意味着运行调用方法 Main 的线程必须等待整整两秒才能获得 BlockingMethod 的结果。如果运行 Main 方法的线程正在处理 UI 重绘,那么这意味着我们得到的 UI 看起来无响应/锁定了整整两秒。我们可以像这样将等待 BlockingMethod 的工作卸载到另一个线程上:

首先,我们将调用方法标记为“async”,因为这会告诉编译器为异步方法中的所有“await”生成类似于状态机的东西:

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

接下来我们将返回类型 'void' 转换为 Task。这允许它被其他线程等待(如果你不关心它,你可以将它保留为 async void,但是具有返回类型的异步方法需要返回 Task):

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

现在我们的方法可以异步运行,并且可以被其他方法等待。但是这还没有解决我们的问题,因为我们还在同步等待阻塞方法。因此,我们通过调用 Task.Run 将阻塞方法移到另一个线程,并等待其结果:

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

但这给我们带来了一个问题:我们的阻塞方法返回了一些我们想稍后在我们的方法中使用的东西,但是 Task.Run 返回了 void!为了访问另一个线程中运行的阻塞方法返回的变量,我们必须在闭包中捕获一个局部变量:

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

所以,总而言之,我们所做的是我们采用了一个普通的同步方法(我们可以重写的调用方法,而不是我们无法重写的第三方 API),我们将其更改为标记为异步,它返回了一个任务。如果我们希望它返回一个结果,那么它需要是一个通用类型的任务。之后,我们将对阻塞方法的调用包装在 Task.Run 中——它为阻塞方法在其上运行创建了一个新线程——然后我们给它一个要运行的 Action(采用 lambda 语法)。在那个 Action 中,我们引用了一个在调用函数(这是闭包)中定义的变量,它可以捕获阻塞方法的结果。

现在,我们正在异步地等待其他地方的同步、阻塞方法。阻塞方法本质上不是异步的并不重要,因为我们让它在另一个线程中同步运行,并允许我们的线程等待它的结果。

如果有任何不清楚的地方,请发表评论。异步起初有点令人困惑,但它是响应式 UI 的天赐之物。

编辑:

作为对 Scott Chamberlain 的评论的回应,Task.Run 也有一个重载,它返回其运行的方法的类型(为其提供 Func,而不是 Action)。所以我们可以简单地使用:

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

请记住——正如 Scott Chamberlain 指出的那样——在另一个线程中运行工作并没有内在的性能优势。在许多情况下,它实际上可能会导致更差的性能,因为设置和拆除线程的成本很高。基于任务的异步仅对保持繁忙的线程(例如 UI 线程)畅通无阻有用,因此它可以响应请求,或者在通过使用例如适本地分割工作时并行.ForEach。

关于c# - 将第 3 方 API 调用转换为异步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19960473/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com