与此答案相关,
如果我真的想要"Fire and Forget"一个确实返回任务的方法,并且(为简单起见)让我们假设该方法不会抛出任何异常.我可以使用答案中列出的扩展方法:
public static void Forget(this Task task)
{
}
Run Code Online (Sandbox Code Playgroud)
使用这种方法,如果存在错误,则会Task
引发异常,然后抛出意外异常时,异常将被吞下并被忽视.
问题:在这种情况下,扩展方法的形式是否更合适:
public static async void Forget(this Task task)
{
await task;
}
Run Code Online (Sandbox Code Playgroud)
因此编程错误会引发异常并升级(通常会导致进程失效).
在具有预期(和可忽略的)异常的方法的情况下,该方法需要变得更加精细(除此之外,关于如何构建此方法的版本的任何建议将采用可接受和可忽略的异常类型的列表? )
如果我需要推迟代码执行,直到UI线程消息循环的未来迭代之后,我可以这样做:
await Task.Factory.StartNew(
() => {
MessageBox.Show("Hello!");
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
这将类似于await Task.Yield(); MessageBox.Show("Hello!");
,除了我有一个选项可以取消任务,如果我想.
在使用默认同步上下文的情况下,我可以类似地使用await Task.Run
继续池线程.
事实上,我喜欢Task.Factory.StartNew
和Task.Run
更多Task.Yield
,因为他们都明确定义了延续代码的范围.
那么,在什么情况下await Task.Yield()
实际上有用呢?
此代码抛出异常.是否可以定义将捕获它的应用程序全局处理程序?
string x = await DoSomethingAsync();
Run Code Online (Sandbox Code Playgroud)
使用.net 4.5/WPF
等待故障任务(具有异常集的任务)时,await
将重新抛出存储的异常.如果存储的异常是a AggregateException
,它将重新抛出第一个并丢弃其余的异常.
我们如何使用await
并同时抛出原件AggregateException
以便我们不会意外丢失错误信息?
注意,当然可以考虑使用hacky解决方案(例如,试一试await
,然后调用Task.Wait
).我真的希望找到一个干净的解决方案.这里最好的做法是什么?
我想过使用自定义awaiter,但内置TaskAwaiter
包含很多魔法,我不知道如何完全重现.它调用TPL类型的内部API.我也不想重现所有这些.
如果你想玩它,这是一个简短的repro:
static void Main()
{
Run().Wait();
}
static async Task Run()
{
Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
await Task.WhenAll(tasks);
}
static Task CreateTask(string message)
{
return Task.Factory.StartNew(() => { throw new Exception(message); });
}
Run Code Online (Sandbox Code Playgroud)
只抛出两个例外中的一个Run
.
请注意,Stack Overflow上的其他问题无法解决此特定问题.建议重复时请小心.
我写了一个HttpListener
侦听其中一个端口:
httpListener.BeginGetContext(new AsyncCallback(ListenerCallback), httpListener);
Run Code Online (Sandbox Code Playgroud)
该ListenerCallback
处理是对听众的URI接收到的任何请求.如果在处理请求期间发生异常,它将运行一个诊断例程,该例程尝试命中侦听器uri以检查侦听器是否实际处于活动状态并侦听uri并写入侦听器返回的响应日志.Listener只是将字符串返回Listening...
给这样的虚拟请求.
现在在测试期间,当导致执行诊断模块的其他模块发生异常时,我可以看到在Listening...
检查日志时监听器正确返回.但是,当发生异常时ListenerCallback
,尝试在诊断内部命中侦听器URI会引发以下异常:
System.Net.WebException : The operation has timed out
at System.Net.HttpWebRequest.GetResponse()
at MyPackage.Diagnostics.hitListenerUrl(String url) in c:\SW\MyApp\MyProj\Diagnostics.cs:line 190
Run Code Online (Sandbox Code Playgroud)
诊断模块中的第190行如下:
189 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
190 HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Run Code Online (Sandbox Code Playgroud)
现在,如果AsyncCallback
调度新线程并ListenerCallback
在该新线程中运行,则Operation Timeout
在通过诊断发送虚拟请求时,不得产生该线程.这就是我认为理想的行为,因为它是*Async*Callback
.事实上MSDN也说同样的话:
使用AsyncCallback委托在单独的线程中处理异步操作的结果.
但似乎并非如此.我在这里错过了什么吗?
视觉解释:
我一直在制作服务器.我在方法中使用TcpListener.AcceptTcpClientAsync()async
,但我不知道如何实际使它工作.我的代码现在是:
private static async void StartServer()
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start();
var client = await listener.AcceptTcpClientAsync();
}
Run Code Online (Sandbox Code Playgroud)
我该如何处理客户?我只是继续编码,它会自动创建相同方法的新线程,还是我需要做一些魔术方法来为我做?
编辑:当前代码:
private static Task HandleClientAsync(TcpClient client)
{
var stream = client.GetStream();
// do stuff
}
/// <summary>
/// Method to be used on seperate thread.
/// </summary>
private static async void RunServerAsync()
{
while (true)
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start(); …
Run Code Online (Sandbox Code Playgroud) 我有一个基于a的ECHO服务器应用程序TCPListener
.它接受客户端,读取数据并返回相同的数据.我使用async/await方法开发了它,使用XXXAsync
框架提供的方法.
我设置了性能计数器来测量输入和输出的消息和字节数,以及连接的插槽数.
我创建了一个启动1400异步的测试应用程序TCPClient
,并且每100-500ms发送一条1Kb消息.客户端在开始时有10-1000毫秒的随机等待启动,因此他们不会尝试同时连接所有客户端.我运作良好,我可以在PerfMonitor中看到1400连接,以良好的速率发送消息.我从另一台计算机上运行客户端应用程序 服务器的CPU和内存使用率非常低,它是带有8Gb RAM的Intel Core i7.客户端看起来比较忙,它是带有4Gb内存的i5,但仍然不是25%.
问题是如果我启动另一个客户端应用程序.连接在客户端中开始失败.我没有看到每秒消息的大幅增加(或多或少增加20%),但我看到连接客户端的数量大约是1900-2100,而不是预期的2800.性能略有下降,图表显示每秒最大和最小消息之间的差异比以前更大.
尽管如此,CPU使用率仍然不是40%,内存使用量仍然很少.我试图在客户端和服务器中增加数量或池线程:
ThreadPool.SetMaxThreads(5000, 5000);
ThreadPool.SetMinThreads(2000, 2000);
Run Code Online (Sandbox Code Playgroud)
在服务器中,循环接受连接:
while(true)
{
var client = await _server.AcceptTcpClientAsync();
HandleClientAsync(client);
}
Run Code Online (Sandbox Code Playgroud)
该HandleClientAsync
函数返回a Task
,但正如您所看到的循环不等待处理,只是继续接受另一个客户端.处理函数是这样的:
public async Task HandleClientAsync(TcpClient client)
{
while(ws.Connected && !_cancellation.IsCancellationRequested)
{
var msg = await ReadMessageAsync(client);
await WriteMessageAsync(client, msg);
}
}
Run Code Online (Sandbox Code Playgroud)
这两个函数只是异步读写流.
我看到我可以开始TCPListener
指示backlog
金额,但默认值是多少?
为什么可能是应用程序在达到最大CPU之前没有扩展的原因?
找出实际问题的方法和工具是什么?
UPDATE
我曾尝试Task.Yield
和Task.Run
办法,他们没有帮助.
服务器和客户端也在同一台计算机上本地运行.每秒增加客户端或消息量实际上会降低服务吞吐量.600个客户端每100ms发送一条消息,产生的吞吐量超过1000个客户端每100ms发送一条消息.
当连接超过~2000个客户端时,我在客户端看到的例外是两个.大约1500年我开始看到例外,但客户最终连接.超过1500我看到很多连接/断开:
"远程主机强行关闭现有连接"(System.Net.Sockets.SocketException)捕获到System.Net.Sockets.SocketException:"远程主机强行关闭现有连接"
"无法将数据写入传输连接:远程主机强制关闭现有连接." (System.IO.IOException)抛出System.IO.IOException:"无法将数据写入传输连接:远程主机强制关闭现有连接."
更新2
我已经使用async/await建立了一个非常简单的服务器和客户端项目,并按预期进行扩展.
我有可扩展性问题的项目就是 …
我最近开始使用新的C#5.0"async"和"await"关键字.我以为我得到了扭曲,但意识到有一件事让我怀疑.这是我从远程TcpClient异步接收数据的方式.一旦我接受连接,我就调用这个函数:
static async void ReadAsync(TcpClient client)
{
NetworkStream ns = client.GetStream();
MemoryStream ms = new MemoryStream();
byte[] buffer = new byte[1024];
while(client.Connected)
{
int bytesRead = await ns.ReadAsync(buffer, 0, buffer.Length);
ms.Write(buffer, 0, bytesRead);
if (!ns.DataAvailable)
{
HandleMessage(ms.ToArray());
ms.Seek(0, SeekOrigin.Begin);
}
}
}
Run Code Online (Sandbox Code Playgroud)
收到数据后,循环就会继续运行而不会读取任何内容.我在循环中用Console.WriteLine测试了这个.我正确使用它吗?我觉得我不应该使用while循环...
我有以下异步TCP服务器,我用它来接受来自想要上传文件的客户端的传入请求.大约1-4小时后,服务器只是停止接受任何新连接.这让我感到困惑,因为我无法确定性地重现错误.程序中的其余线程继续正常运行.没有任何例外.关于可能发生什么的任何建议?
在服务器崩溃之前,我所看到的是它设法完成待处理的请求然后停止.我怀疑服务器是否经常出现网络断开连接.如何使这段代码对故障更加健壮?我已经将try-catch添加到可能会失败的两段代码中,但我觉得我仍然缺少某些东西.
我设置另一个线程来检查此服务器是否释放套接字,但即使在它停止处理客户端请求后,套接字似乎仍在使用中.我用这个验证了这个netstat -a
internal class TCPServer
{
private readonly int _listeningPort;
private TcpListener listener;
public TCPServer(int port)
{
_listeningPort = port;
listener = new TcpListener(IPAddress.Any, _listeningPort);
listener.Start(int.MaxValue);
}
public async void Start()
{
while (true)
{
Log.Verbose("Waiting for connections...");
try
{
var tcpClient = await listener.AcceptTcpClientAsync();
Task t = HandleConnectionAsync(tcpClient);
await t;
}
catch (Exception exp)
{
Log.Fatal(exp.ToString());
}
}
}
private async Task HandleConnectionAsync(TcpClient tcpClient)
{
try
{
string outputFile = ""; // get a random string …
Run Code Online (Sandbox Code Playgroud) 我想UDPListener
在后台运行它:
// launch this in a background thread
private static void UDPListen()
{
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0)
using( var udpClient = new UdpClient(10000))
{
while (true)
{
var buffer = udpClient.Receive(ref remoteEndPoint);
var loggingEvent = System.Text.Encoding.Unicode.GetString(buffer);
// ...
}
}
}
Run Code Online (Sandbox Code Playgroud)
我可以把它放在一个Task.Run()
并在while()
循环中运行吗?
c# ×9
async-await ×7
.net ×5
asynchronous ×5
tcp ×3
tcplistener ×2
c#-5.0 ×1
exception ×1
http ×1
log4net ×1
tcpclient ×1