简体   繁体   English

使用C#的新异步功能等待网络数据包的最佳方法是什么

[英]What is the best way to wait on a network packet using C#'s new async feature

I've recently been playing around with the new Async CTP, and I've come across a situation where I'm not sure how to proceed. 我最近一直在玩新的Async CTP,我遇到过一种我不确定如何继续的情况。

In my current code base, I'm using a concept of "jobs" and a "job manager". 在我目前的代码库中,我使用的是“作业”和“作业管理器”的概念。 Jobs exist solely for the purpose of handling an initial message, sending a response, and then waiting the response. 作业仅用于处理初始消息,发送响应,然后等待响应。

I already have existing code based around synchronous sockets, where a network thread is waiting on data to arrive, and then passing it along to an event handler, and eventually to the job manager. 我已经有了基于同步套接字的现有代码,其中网络线程正在等待数据到达,然后将其传递给事件处理程序,最终传递给作业管理器。

The job manager looks for what job would handle the message, and passes it along. 作业管理器查找将处理消息的作业,并将其传递。

So the scenario is this: 所以场景是这样的:

  1. Job manager gets a new message and launches a job. 工作经理获得新消息并启动工作。
  2. The job starts, processes the message, and sends a reply message. 作业启动,处理消息并发送回复消息。
  3. At this point the job would wait for a response to the reply. 此时,作业将等待对回复的响应。

Here's a pseudocode example: 这是一个伪代码示例:

class MyJob : Job
{
    public override void RunJob( IPacketMsg packet )
    {
        // handle packet

        var myReply = new Packet();
        SendReply( myReply );

        await GetResponse();
    }
}

But I'm not entirely sure how to proceed at step 3. The job manager will get the response and then hand it along to the running job. 但我不完全确定如何在第3步继续。工作经理将获得响应,然后将其交给正在运行的工作。 But I'm not sure how to make the job wait for the response. 但我不知道如何让工作等待回应。

I've considered creating an awaited Task that simply blocks on a WaitHandle, but is this the best solution? 我考虑过创建一个等待在WaitHandle上阻塞的任务,但这是最好的解决方案吗?

Are there any other things I could do in this case? 在这种情况下我还能做些什么吗?

Edit On the subject of the Async CTP, what happens in a situation where the UI is not being used. 编辑关于Async CTP的主题,在未使用UI的情况下会发生什么。 I've read over Eric Lippert's Async blog, but I don't believe it ever touched on the subject of how everything works in the background without a UI thread (does it spin off a background worker or...?) 我已经阅读了Eric Lippert的Async博客,但我不相信它曾经触及过如何在没有UI线程的情况下在后台工作的主题(它是关闭背景工作者还是......?)

  1. Job manager gets a new message and launches a job. 工作经理获得新消息并启动工作。
  2. The job starts, processes the message, and sends a reply message. 作业启动,处理消息并发送回复消息。
  3. At this point the job would wait for a response to the reply. 此时,作业将等待对回复的响应。

First off, I should mention that the Async CTP handles asynchronous operations very well, but asynchronous events not so much. 首先,我应该提到Async CTP非常好地处理异步操作 ,但异步事件并没有那么多。 You may want to consider an Rx-based approach. 您可能想要考虑基于Rx的方法。 But let's proceed for the moment with the Async CTP. 但是让我们继续使用Async CTP。

You have two basic options to create Tasks: 您有两个基本选项来创建任务:

  • With a delegate. 有代表。 eg, Task.Factory.StartNew will run a delegate on the thread pool. 例如, Task.Factory.StartNew将在线程池上运行委托。 Custom task factories and schedulers give you more options for task delegates (eg, specifying the delegate must be run on an STA thread). 自定义任务工厂和调度程序为任务委托提供了更多选项(例如,指定委托必须在STA线程上运行)。
  • Without a delegate. 没有代表。 eg, TaskFactory.FromAsync wraps an existing Begin / End method pair, TaskEx.FromResult returns a "future constant", and TaskCompletionSource can be used to control a Task explicitly (both FromAsync and FromResult use TCS internally). 例如, TaskFactory.FromAsync包装现有的Begin / End方法对, TaskEx.FromResult返回“future constant”, TaskCompletionSource可用于显式控制TaskFromAsyncFromResult都在内部使用TCS )。

If the job processing is CPU-bound, it makes sense to pass it off to Task.Factory.StartNew . 如果作业处理受CPU约束,则将其传递给Task.Factory.StartNew I'm going to assume the job processing is CPU-bound. 我将假设作业处理受CPU限制。

Job manager pseudo-code: 工作管理员伪代码:

// Responds to a new message by starting a new job on the thread pool.
private void RespondToNewMessage(IPacketMsg message)
{
  IJob job = ..;
  Task.Factory.StartNew(job.RunJob(message));
}

// Holds tasks waiting for a response.
private ConcurrentDictionary<int, TaskCompletionSource<IResponse>> responseTasks = ..;

// Asynchronously gets a response for the specified reply.
public Task<IResponse> GetResponseForReplyAsync(int replyId)
{
  var tcs = new TaskCompletionSource<IResponse>();
  responseTasks.Add(replyId, tcs);
  return tcs.Task;
}

// Responds to a new response by completing and removing its task.
private void RespondToResponse(IResponse response)
{
  var tcs = responseTasks[response.ReplyId];
  responseTasks.Remove(response.ReplyId);
  tcs.TrySetComplete(response);
}

The idea is that the job manager also manages a list of oustanding responses. 这个想法是,工作经理还管理一系列优秀的回复。 In order for this to happen, I introduced a simple int reply identifier that the job manager can use to determine which response goes with which reply. 为了实现这一点,我引入了一个简单的int reply标识符,作业管理器可以使用该标识符来确定响应哪个响应。

Now jobs can work like this: 现在工作可以像这样工作:

public override void RunJob(IPacketMsg packet)
{
  // handle packet
  var myReply = new Packet();
  var response = jobManager.GetResponseForReplyAsync(myReply.ReplyId);
  SendReply(myReply);

  await response;
}

There's a few tricky things since we're placing the jobs on the thread pool thread: 因为我们将作业放在线程池线程上,所以有一些棘手的事情:

  1. GetResponseForReplyAsync must be invoked (registering the task) before the reply is sent, and is then await ed later. 必须在发送回复之前调用 GetResponseForReplyAsync (注册任务),然后稍后await This is to avoid the situation where a reply may be sent and a response received before we have a chance to register for it. 这是为了避免在我们有机会注册之前发送回复和收到回复的情况。
  2. RespondToResponse will remove the task registration before completing it, just in case completing the task causes another reply to be sent with the same id. RespondToResponse将在完成任务之前删除任务注册,以防万一完成任务导致另一个回复以相同的id发送。

If the jobs are short enough that they don't need to be placed on the thread pool thread, then the solution can be simplified. 如果作业足够短,不需要将它们放在线程池线程上,那么可以简化解决方案。

On the subject of the Async CTP, what happens in a situation where the UI is not being used. 关于Async CTP的主题,在没有使用UI的情况下会发生什么。 I've read over Eric Lippert's Async blog, but I don't believe it ever touched on the subject of how everything works in the background without a UI thread (does it spin off a background worker or...?) 我已经阅读了Eric Lippert的Async博客,但我不相信它曾经触及过如何在没有UI线程的情况下在后台工作的主题(它是关闭背景工作者还是......?)

await will return to its execution context. await将返回其执行上下文。 In a UI process, this is a UI message loop. 在UI过程中,这是一个UI消息循环。 In ASP.NET, this is the ASP.NET thread pool. 在ASP.NET中,这是ASP.NET线程池。 In other situations (Console applications and Win32 services), there is no context, so continuations are queued to the ThreadPool . 在其他情况下(控制台应用程序和Win32服务),没有上下文,因此continuation排队到ThreadPool This is not usually desired behavior, so I wrote an AsyncContext class that can be used in those situations. 这通常不是所希望的行为,因此我编写了一个可以在这些情况下使用的AsyncContext类。

BackgroundWorker is not used. 不使用BackgroundWorker In a server-side scenario such as yours, it's not uncommon to not have a background thread at all. 在像你这样的服务器端场景中,根本没有后台线程并不罕见。

You would simply wire up the rest of your event handler with the await pattern like so: 您只需使用await模式连接事件处理程序的其余部分,如下所示:

 public async void RunJob(IPacketMsg msg)
 {
     // Do Stuff

     var response = await GetResponse();

     // response is "string", not "Task<string>"

     // Do More Stuff
 }

 public Task<string> GetResponse()
 {
     return Task.Factory.StartNew(() =>
        {
             _networkThingy.WaitForDataAvailable();

             return _networkThingy.ResponseString;
        });
 }

When your get response task finishes, the rest of the method picks up execution on your current synchronization context. 当get响应任务完成时,方法的其余部分将在当前同步上下文中执行。 Until then, however, your method execution is yielded (so any code after the wait is not run until the task started in GetResponse finishes) 但是,在此之前,您的方法执行已经产生(因此,在GetResponse中启动的任务完成之前,等待之后的任何代码都不会运行)

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

相关问题 C# - 使用 TcpListener(异步)的最佳方式是什么 - C# - What's the best way to use TcpListener (async) 在C#中创建网络数据包结构/类的最佳方法是什么? - What is the best method for creating a network packet struct/class in C#? 使用C#通过网络发送文件的最佳方法是什么? - What's the best way to send a file over a network using C#? 在C#中等待的最佳方法是什么? - What is the best way to wait in C#? 什么是新的C#异步功能的非网络示例? - What's a good non-networked example of the new C# Async feature? 在C#中从非异步调用异步代码的最佳方法是什么(Fire和Forget) - what's the best way to invoke Async Code from Non Async in C# (Fire and Forget) 使用Roslyn突出显示C#语法的最佳方法是什么? - What's the best way to highlight C# syntax using Roslyn? 使用C#以编程方式测试计算机是否在公司(AD)网络上的最佳方法是什么? - What's the best way to programmatically test if a computer is on your corporate (AD) network with C#? .net c#等待异步事件完成的最佳方法,并且代码中仍然有“同步”流 - .net c# best way to wait for async events to complete and still have a “synchronous” flow in your code 在 Linqpad 中使用 C# 5 异步功能 - Using C# 5 async feature in Linqpad
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM