C# 此模式是否具有与node.js类似的生命周期,是否存在任何问题?
我正在熟悉C#中的C# 此模式是否具有与node.js类似的生命周期,是否存在任何问题?,c#,queue,async-await,nonblocking,worker,C#,Queue,Async Await,Nonblocking,Worker,我正在熟悉C#中的async关键字,以及图书馆生态系统,以寻求一个实用的解决方案 我的研究得出以下静态主循环代码: Task loop = Task.Factory.StartNew(async () => { try { using(RedisConnection redis = new RedisConnection("localhost")) { var queue = "my_queue"; var res
async
关键字,以及图书馆生态系统,以寻求一个实用的解决方案
我的研究得出以下静态主循环代码:
Task loop = Task.Factory.StartNew(async () => {
try {
using(RedisConnection redis = new RedisConnection("localhost")) {
var queue = "my_queue";
var reserved = string.Concat(queue, "_reserved");
redis.Open();
while(true) {
Task.Factory.StartNew(async () => {
var pushRequest = await redis.Lists.RemoveLastAndAddFirstString(0, queue, reserved);
});
}
}
}
catch(Exception ex) {
Console.Error.WriteLineAsync(ex.Message);
}
}, cancellationToken);
loop.Wait();
如您所见,我正在尝试创建一个非阻塞工作程序。我觉得我已经接近了核心循环代码本身的完整解决方案,很明显,内部Task.Factory.StartNew
调用中的内容将开始转移到单独的类和方法中
我有几个问题:
- 我现在在这里的任何内容都应该从静态main方法中移出吗
- 我注意到,当我运行这段代码时,我的CPU活动增加了一点,我假设是因为我正在创建多个连续体,请求redis提供信息。这是正常的还是我应该减轻这一点
- 在这一点上,这真的是异步/非阻塞的吗?我的内部lambda中的代码不会占用我的主工作线程吗
- C#是否为continuations生成额外的线程
为可能出现的业余错误道歉。正如我所提到的,我仍在学习,但我希望从社区获得最后一点指导。您不需要额外的池线程来执行IO绑定操作(关于这方面的更多想法)。在这种情况下,重新分解的代码可能如下所示:
async Task AsyncLoop(CancellationToken cancellationToken)
{
using (RedisConnection redis = new RedisConnection("localhost"))
{
var queue = "my_queue";
var reserved = string.Concat(queue, "_reserved");
redis.Open();
while (true)
{
// observe cancellation requests
cancellationToken.ThrowIfCancellationRequested();
var pushRequestTask = redis.Lists.RemoveLastAndAddFirstString(0, queue, reserved);
// continue on a random thread after await,
// thanks to ConfigureAwait(false)
var pushRequest = await pushRequestTask.ConfigureAwait(false);
// process pushRequest
}
}
}
void Loop(CancellationToken cancellationToken)
{
try
{
AsyncLoop().Wait();
}
catch (Exception ex)
{
while (ex is AggregateException && ex.InnerException != null)
ex = ex.InnerException;
// might be: await Console.Error.WriteLineAsync(ex.Message),
// but you cannot use await inside catch, so:
Console.Error.WriteLine(ex.Message);
}
}
注意,缺少Task.Run
/Task.Factory.StartNew
并不意味着总是会有相同的线程。在本例中,由于ConfigureAwait(false)
,因此await
之后的代码很可能会在不同的线程上继续。但是,如果调用线程上没有同步上下文(例如,对于控制台应用程序),则此ConfigureAwait(false)
可能是多余的。要了解更多信息,请参阅中列出的文章,特别是Stephen Cleary的文章
可以在AsyncLoop
中复制Node.js事件循环的单线程、线程仿射行为,因此wait
之后的每个延续都在同一线程上执行。您将使用自定义任务计划程序(或自定义SynchronizationContext
和TaskScheduler.FromCurrentSynchronizationContext
)。这并不难实现,尽管我不确定这是否是您所要求的
如果这是一个服务器端应用程序,在同一个线程a-la Node.js上序列化await
continuations可能会损害应用程序的可伸缩性(特别是如果在awaits
之间有一些CPU限制的工作)
要解决您的具体问题:
我现在在这里的任何东西都应该从静态中移出吗
主要方法
如果您使用的是一系列的async
方法,那么在最顶层的堆栈框架中将有一个异步到-任务的转换。对于控制台应用程序,可以在Main
中使用Task.Wait()
阻止,这是最上面的入口点
我注意到当我运行这段代码时,我的CPU活动增加了一点,我
假设因为我正在创建多个continuations请求redis
供参考。这是正常的还是我应该减轻这一点
很难说清楚为什么你的CPU活动会增加。我宁愿责怪这个应用程序的服务器端,它在localhost
上侦听和处理redis请求
在这一点上,这真的是异步/非阻塞的吗
在我的内部lambda从来没有绑住我的主要工作线程
如果new RedisConnection
是非阻塞的,并且RemoveLastAndAddFirstString
不会阻塞其同步部分内的任何地方,那么我发布的代码确实是非阻塞的
C#是否为continuations生成额外的线程
不明确。异步IO操作处于“运行中”时没有线程。但是,当操作完成时,将有一个来自ThreadPool
的IOCP线程被分配来处理完成。wait
之后的代码是否在此线程或原始线程上继续,取决于同步上下文。如前所述,可以使用ConfigureAwait(false)
控制此行为。您将永远不断生成新的侦听器。这不是一个好主意。你能建议我如何或在哪里解决这个问题,以便它只尝试在获得价值后工作吗?非常好的信息,非常感谢!我认为还值得一提的是,我认为虽然节点是“单线程的”,但我所读到的信息表明,它只是一个从不阻塞的工作者。其他操作有时可能会产生一个新线程,它只是主逻辑,永远不会被阻塞。这可能会使您在此建议的代码与node.js方法几乎相同?另外,请注意。AsyncLoop方法是否应该在某处返回任务?@Omega,事实上,只有在任何地方有阻塞块(例如,同步DNS解析)时,工作进程才会阻塞。每当发生这种情况并且没有合适的自然异步API可供使用时,您可以使用wait Task.Run()
包装此类调用。所以AsyncLoop
类似于节点的事件循环,除了await
continuations没有线程关联之外<代码>异步循环
不必显式返回任何内容,因为存在异步任务
签名(异步
缺失,已修复)。C编译器使其返回一个任务
。如果没有async
,它将不得不这样做,但您不能在它里面使用wait
。非常感谢您的帮助,这里非常清晰。@Omega,它是安全的。CPU和内存的使用实际上取决于“RemovelastandDdfirstString内部的情况。尝试
等待任务。改为延迟(500)`并比较您的里程数。