我是C#
await/async
的新手,目前正在玩一下。
在我的场景中,我有一个简单的客户端对象,它具有WebRequest
属性。客户端应定期通过WebRequest
RequestStream
发送活动消息。
这是client-object的构造函数:
public Client()
{
_webRequest = WebRequest.Create("some url");
_webRequest.Method = "POST";
IsRunning = true;
// --> how to start the 'async' method (see below)
}
和async alive-sender方法
private async void SendAliveMessageAsync()
{
const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
var seconds = 0;
while (IsRunning)
{
if (seconds % 10 == 0)
{
await new StreamWriter(_webRequest.GetRequestStream()).WriteLineAsync(keepAliveMessage);
}
await Task.Delay(1000);
seconds++;
}
}
该方法应该如何开始?
new Thread(SendAliveMessageAsync).Start();
或
Task.Run(SendAliveMessageAsync); //将返回类型更改为Task
或
await SendAliveMessageAsync(); //失败,因为构造函数不是异步
我的问题更多是关于我对await/async
的个人理解,我认为在某些方面可能是错误的。
第三个选项是抛出
The 'await' operator can only be used in a method or lambda marked with the 'async' modifier
答案 0 :(得分:7)
该方法应该如何开始?
我投票赞成以上所有"。 :)
"火与忘记"是一个难以正确处理的方案。特别是,错误处理总是有问题的。在这种情况下,async void
可能会让您感到惊讶。
如果我不立即await
,我更愿意明确保存这些任务:
private async Task SendAliveMessageAsync();
public Task KeepaliveTask { get; private set; }
public Client()
{
...
KeepaliveTask = SendAliveMessageAsync();
}
这至少允许Client
的使用者检测并从SendAliveMessageAsync
方法抛出的异常中恢复。
另一方面,这种模式几乎等同于我的documentation。
答案 1 :(得分:3)
之前的回答是错误的:
正如构造函数中的那样,我认为你必须为它启动一个新的线程。我个人会这样做
Task.Factory.StartNew(()=> SendAliveMessageAsync());
答案 2 :(得分:0)
由于它是 fire and forget 操作,您应该使用
启动它SendAliveMessageAsync();
请注意,await
无法启动新的Task
。等待Task
完成只是语法糖
使用Task.Run
启动新线程。
所以在SendAliveMessageAsync
内你应该开始一个新的任务:
private async Task SendAliveMessageAsync()
{
const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
await Task.Run( () => {
var seconds = 0;
while (IsRunning)
{
if (seconds % 10 == 0)
{
await new StreamWriter(_webRequest.GetRequestStream()).WriteLineAsync(keepAliveMessage);
}
await Task.Delay(1000);
seconds++;
}
});
}
答案 3 :(得分:0)
这是一个选项,因为您无法从构造函数中调用await
。
我建议使用Microsoft的Reactive Framework(NuGet" Rx-Main")。
代码如下所示:
public class Client
{
System.Net.WebRequest _webRequest = null;
IDisposable _subscription = null;
public Client()
{
_webRequest = System.Net.WebRequest.Create("some url");
_webRequest.Method = "POST";
const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
var keepAlives =
from n in Observable.Interval(TimeSpan.FromSeconds(10.0))
from u in Observable.Using(
() => new StreamWriter(_webRequest.GetRequestStream()),
sw => Observable.FromAsync(() => sw.WriteLineAsync(keepAliveMessage)))
select u;
_subscription = keepAlives.Subscribe();
}
}
此代码处理所需的所有线程并正确处理StreamWriter
。
每当你想要停止保持活动时,只需致电_subscription.Dispose()
。
答案 4 :(得分:0)
在你的代码中,不需要使用async / await,只需设置一个新线程来执行长操作。
private void SendAliveMessage()
{
const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
var sreamWriter = new StreamWriter(_webRequest.GetRequestStream());
while (IsRunning)
{
sreamWriter.WriteLine(keepAliveMessage);
Thread.Sleep(10 * 1000);
}
}
使用Task.Factory.StartNew(SendAliveMessage, TaskCreationOptions.LongRunning)
执行操作。
如果你真的想使用async / await模式,只需在构造函数中调用它而不使用await修饰符并忘记它。
public Client()
{
_webRequest = WebRequest.Create("some url");
_webRequest.Method = "POST";
IsRunning = true;
SendAliveMessageAsync(); //just call it and forget it.
}
我认为设置一个长时间运行的线程或使用async / await模式并不是一个好主意。定时器可能更适合这种情况。