在C#中如何在同一线程中异步运行方法

3
在与调用者相同的线程中异步定义和调用方法是否可能?假设我只有一个核心,不想使用像100个线程之类的线程管理开销。
编辑:我之所以问是因为nodejs的模式 - 所有东西都在一个线程上,永远不会阻塞任何东西,这被证明非常有效,这让我想知道在C#中是否可能实现相同的内容(我自己无法实现)。
编辑2:正如评论中所指出的那样,node并不是单线程的(但简单的负载测试表明,它只使用了一个核心...),但我认为使其如此高效的是隐含的要求仅编写非阻塞代码。这在C#中是可能的,只是不是必需的:) 无论如何,谢谢大家...
更多信息请参见this SO post,甚至可以在this one中找到更多信息。

6
假设异步操作不是CPU密集型的,比如等待三十秒然后重新设置按钮的颜色,为什么需要再启动一个线程呢? - Eric Lippert
7
@ren:话说,Matt Ball说得有道理。是否需要将所讨论的操作运行在另一个线程上取决于该操作的实现者。如果你想限制创建的线程数量,那就要用到线程池。如果你生成异步任务,TPL会负责从线程池中调度正确数量的线程以充分利用核心。与其让我们继续猜测你在做什么,不如你提出一个关于实际问题的问题,而不是提出一种解决方案。 - Eric Lippert
4
如果你的问题是“我能编写一个每个操作都是异步的程序,而且每个操作都在同一线程上吗?”那么答案是肯定的,当然你可以这样做。如果是编写异步操作的人,那么决定它运行的线程。如果你的问题是“我有这个方法ComputeFractalAsync,它使用线程池线程,我想让它在当前线程上运行”,那么请联系编写此方法的人,并要求他们编写一个不使用线程池的版本。 - Eric Lippert
1
Node.js并不完全是单线程的。JS实际上被同步到事件函数中,因此您看不到多线程。还可以阅读“NodeJS真的是单线程的吗?”和“Node不是单线程的”这两篇文章。 - metadings
3
如果你希望所有的调用都在同一个线程中,并且你正在编写所有的代码,那么永远不要创建新的线程。就这么简单。 - Eric Lippert
显示剩余4条评论
1个回答

9

我不太清楚你所说的具体上下文是什么,但 C# 5 的 async/await 特性已经帮助支持此类需求。不同之处在于,Node.js 中默认强制所有内容都是单线程运行(以我理解为准,评论中的链接可能会有不同意见),而使用异步编程的 .NET 服务器端应用程序将使用很少的线程而不限制它们。如果确实可以通过单个线程处理所有内容,那就可以这样做 - 如果您从物理处理的角度来看永远只有一件事情要做,那就可以这样。

但是,如果一个请求过来时另一个正在执行一些工作怎么办?也许只是进行一些小量的加密之类的工作。您真的想让第二个请求等待第一个请求完成吗?如果是这样,您可以使用与单线程线程池相关联的 TaskScheduler 在 .NET 中轻松建模... 但更常见的做法是使用内置的 .NET 线程池,它可以高效地工作并允许并发。

首先,您应该确保使用的是 .NET 4.5 - 它比之前版本的 .NET 具有更多的异步 API(例如用于数据库和文件访问)。您需要使用符合任务并行模式(TAP)的 API。然后,使用async/await,您可以编写服务器端代码,使其看起来像同步代码一样,但实际上执行是异步的。例如,您可以编写:

Guid userId = await FetchUserIdAsync();
IEnumerable<Message> messages = await FetchMessagesAsync(userId);

尽管每个操作都需要“等待”,但您可以在不阻塞线程的情况下执行它们。C#编译器为您构建状态机,无需显式编写回调函数,这通常会导致代码混乱 - 编译器完成所有操作。
您需要确保所使用的任何Web / Web服务框架都支持异步操作,并尽可能少地使用线程来管理其余部分。
请点击上面的两个链接以了解C# 5中的异步支持 - 这是一个巨大的话题,我相信您在阅读更多内容后将有更多问题,但是当您阅读更多内容后,您应该能够提出非常具体的问题而不是当前广泛的问题。

对于多线程,我非常推荐Joseph Albahari的C#中的线程 - metadings
7
这确实是一个很棒的教程,但在异步世界中,很多内容都不再相关。在这个世界里,大部分时间你不需要显式地启动新线程或新任务。虽然异步和并发有关联,但它们并不是同一个概念。 - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接