C# 异步和 Java ExecutorService 之间的区别

2022-09-03 16:43:58

C# 有一个很酷的新功能

public Task<string> async f()
{
    string r = LongCompute();
    return r;
}

但这不等同于

public Future<String> f() {
    return Globals.executorService.submit(new Callable<String>() {
        public String call() throws Exception {
            String r = longCompute();
            return r;
        }
    });
}

在Java中,您可以更灵活地选择运行任务的线程池。

那么等待呢?这相当于只是调用 get

string s = await f();

就像

String s = f().get();

C#还有什么,或者它确实只是Java版本的语法糖?(我不是C#大师,所以我可能会错过一些东西)。


答案 1

,不像只是打电话。还有更多的东西。awaitget()

在 C# 中使用表达式时,编译器会有效地创建一个延续,因此,如果 awaitable 尚未完成,则该方法可以立即返回,并且仅在完成后继续处理。延续将在适当的上下文中运行 - 因此,如果你在表达式之前的 UI 线程上,则稍后将继续使用 UI 线程,但在等待结果时不会阻塞 UI 线程。例如:awaitawait

public async void HandleButtonClick(object sender, EventArgs e)
{
    // All of this method will run in the UI thread, which it needs
    // to as it touches the UI... however, it won't block when it does
    // the web operation.

    string url = urlTextBox.Text;
    WebClient client = new WebClient();
    string webText = await client.DownloadStringTaskAsync(url);

    // Continuation... automatically called in the UI thread, with appropriate
    // context (local variables etc) which we used earlier.
    sizeTextBox.Text = string.Format("{0}: {1}", url, webText.Length); 
}

最终,这都是句法糖,但比你所展示的要复杂得多

网络上已经有很多详细信息。例如:


答案 2

乔恩没有解释真正的观点

Java是基于线程的,而C#可以说是基于纤维的ExecutorServiceawait

两者都允许多任务处理,这将计算资源分配到并发函数(即“同时运行的函数”)之间。第一种多任务处理称为先发制人,而第二种合作。从历史上看,先发制人的多任务处理被认为更先进,优于合作。事实上,在消费者操作系统支持抢占式多任务处理之前,计算机真的很糟糕。但是,抢占式多任务处理有其缺点。它可能很难编程,并且会占用更多内存。

两者之间的主要区别在于,抢占式多任务处理允许运行时(通常是操作系统本身)随时停止任何功能并启动不同的功能(并在不同的CPU上同时运行它们)。同时,协同多任务处理需要运行功能结束或自愿暂停。我们大多数人都熟悉多线程形式的抢占式多任务处理,以及随之而来的那种精心编程。很少有人熟悉协作式多任务处理,现在通常被称为纤程或协程(在这种情况下,它是在抢占式操作系统线程内的用户空间中实现的)。

无论如何,关键是并且没有直接的可比性,并且通常不优于真正的多线程(除了它具有很好的语法糖)。C#包含(并基于协作式多任务处理)的原因是平台上的主要GUI工具包不是为多线程而设计的,重构它们以支持并发性需要大量工作。协作式多任务处理适用于 UI,因为大多数事件处理程序都很短,可以按顺序执行。 通过让长事件处理程序在呈现函数有机会运行后暂停自身并恢复,扩展了事件循环的概念。所有这些都发生在单个CPU内核上的单个线程中。ExecutorServiceawaitawaitawaitawait

它们都能找到共同点,因为它们都是多任务处理的形式,而且都是同步的形式。Future.getawait Task

可以预料,C# 并非没有对线程和线程池的良好支持。同样,Java 在许多库中包含纤/协同例程/异步,例如 Servlet 3.0 和 .javafx.concurrent.Task

回应Jon Skeet:延续(如纤维的用户空间实现机制)并非易事,但线程在实现方面同样复杂。Jon 可能因为线程背后的算法在操作系统中而不是在编译器或 .NET 运行时中而被抛弃。