这不是"如何在没有等待的情况下安全地调用C#中的异步方法"的副本.
我如何很好地抑制以下警告?
警告CS4014:由于未等待此调用,因此在完成调用之前,将继续执行当前方法.考虑将'await'运算符应用于调用的结果.
一个简单的例子:
static async Task WorkAsync()
{
await Task.Delay(1000);
Console.WriteLine("Done!");
}
static async Task StartWorkAsync()
{
WorkAsync(); // I want fire-and-forget
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
}
Run Code Online (Sandbox Code Playgroud)
我尝试过但不喜欢的事情:
static async Task StartWorkAsync()
{
#pragma warning disable 4014
WorkAsync(); // I want fire-and-forget here
#pragma warning restore 4014
// ...
}
static async Task StartWorkAsync()
{
var ignoreMe = WorkAsync(); // I want fire-and-forget here
// ...
}
Run Code Online (Sandbox Code Playgroud)
更新后,由于原始接受的答案 …
以下两段代码之间是否存在任何概念差异:
async Task TestAsync()
{
await Task.Run(() => DoSomeWork());
}
Run Code Online (Sandbox Code Playgroud)
和
Task TestAsync()
{
return Task.Run(() => DoSomeWork());
}
Run Code Online (Sandbox Code Playgroud)
生成的代码也不同吗?
编辑:为避免混淆Task.Run,类似的情况:
async Task TestAsync()
{
await Task.Delay(1000);
}
Run Code Online (Sandbox Code Playgroud)
和
Task TestAsync()
{
return Task.Delay(1000);
}
Run Code Online (Sandbox Code Playgroud)
最新更新:除了接受的答案之外,LocalCallContext处理方式也有所不同:即使没有异步,CallContext.LogicalGetData也会被恢复.为什么?
考虑这个例子:
var task = DoSomething()
bool ready = await DoSomethingElse();
if (!ready)
return null;
var value = await DoThirdThing(); // depends on DoSomethingElse
return value + await task;
Run Code Online (Sandbox Code Playgroud)
DoSomething做一些可能需要一段时间的非常重要的工作,因此我们首先开始它.
与此同时,我们检查我们是否准备就绪,DoSomethingElse如果没有,我们会提前退出.
我们DoThirdThing 只有在我们这样做时才会打电话ready,因为宇宙可能会爆炸.
我们不能用Task.WhenAll作为DoThirdThing依赖DoSomethingElse,我们也不想等待DoSomething,因为我们希望尽可能地其他两种方法同时调用.
问题:发生什么task,如果我们不能ready和退出早?
它抛出的任何异常会被重新抛出SynchronizationContext吗?
如果task正常完成会有问题吗,因为没有人消耗它的价值?
后续行动:有没有一种简洁的方法可以确保task等待?
我们可以简单地说,await task如果我们不是ready,但如果有50个退出条件,这将是非常繁琐的.
可以使用finally块来await task重新抛出潜在的异常吗?如果task正常完成,将在finally块中再次等待,但这不应该导致任何问题?
如果我需要推迟代码执行,直到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()实际上有用呢?
我正在使用同步apis和线程池的tcp服务器上看起来像这样:
TcpListener listener;
void Serve(){
while(true){
var client = listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(this.HandleConnection, client);
//Or alternatively new Thread(HandleConnection).Start(client)
}
}
Run Code Online (Sandbox Code Playgroud)
假设我的目标是在资源使用率最低的情况下处理尽可能多的并发连接,这似乎很快就会受到可用线程数量的限制.我怀疑通过使用非阻塞任务apis,我将能够用更少的资源处理更多.
我最初的印象是这样的:
async Task Serve(){
while(true){
var client = await listener.AcceptTcpClientAsync();
HandleConnectionAsync(client); //fire and forget?
}
}
Run Code Online (Sandbox Code Playgroud)
但令我印象深刻的是,这可能会导致瓶颈.也许HandleConnectionAsync需要花费非常长的时间才能达到第一次等待,并且将阻止主接受循环继续进行.这只会使用一个线程,还是运行时会在多个线程上神奇地运行它看起来合适的东西?
有没有办法结合这两种方法,以便我的服务器将使用它所需的线程数量来确定正在运行的任务的数量,但是它不会在IO操作上不必要地阻塞线程?
在这样的情况下,是否存在最大化吞吐量的惯用方法?
自开始以来,我们一直在我们的项目中使用xUnit Framework作为测试框架.目前在项目中有2200多个单元测试,一切似乎都很好.
但昨天我决定在CI版本和夜间版本中运行单元测试.与团队构建控制器战斗运行xunit测试1-2小时我成功运行测试.但是有一个问题,有22个警告关于下面的测试, - Xunit.Sdk.EqualException:Assert.Equal()失败或--System.ArgumentNullException:值不能为null.
经过一些研究后,我意识到这些测试必须失败但似乎已经通过,因为所有这些测试都标有"async"关键字.
实际上应该没有任何错误,因为xUnit支持关于这些帖子的异步测试 http://bradwilson.typepad.com/blog/2012/01/xunit19.html http://sravi-kiran.blogspot.com.tr/ 2012/11 / UnitTestingAsynchronousMethodsUsingMSTestAndXUnit.html Howerver现实对我来说是微不足道的.是的xUnit运行没有任何问题,但没有正确测试.
这是两个小方法.
public async Task<string> AsyncTestMethod() {
throw new Exception("Test");
}
public string NormalTestMethod() {
throw new Exception("Test");
}
Run Code Online (Sandbox Code Playgroud)
正如您只能看到差异,第一个方法定义为"async",这里是对这些方法的两个测试.
[Fact]
public async void XunitTestMethod_Async() {
Program p = new Program();
string result = await p.AsyncTestMethod();
Assert.Equal("Ok", result);
}
[Fact]
public void XunitTestMethod_Normal() {
Program p = new Program();
string result = p.NormalTestMethod();
Assert.Equal("Ok", result);
}
Run Code Online (Sandbox Code Playgroud)
两个原始方法都抛出异常,所以我认为两个测试都会失败但结果却不同.你可以看到结果:

XunitTestMethod_Async测试通过,但XunitTestMethod_Normal失败.抛出异常并非如此,您可以根据需要更改方法内容AsyncTestMethod始终传递.
以下是示例解决方案:https://github.com/bahadirarslan/AsyncTestWithxUnit
我可能会误解或想错,但现在这种行为给我带来了很多痛苦
希望你能引起我的注意.
PS:我在xUnit github页面上创建了一个问题,但我无法确定这是由我还是xUnit造成的.所以我决定在这里问一下.问题:https: …
我最近一直在处理异步等待(阅读包括Stephen和Jon最后两章在内的所有可能的文章),但我得出结论,我不知道它是否100%正确. - 因此我的问题.
由于 async只允许单词等待出现,我将async不谈.
AFAIU等待着继续.而不是编写功能(连续)代码,编写同步代码.(我想把它称为可回调代码)
因此,当编译器到达时await- 它将代码分成2个部分,并在第一部分完成后注册要执行的第二部分(我不知道为什么callback不使用该字- 这正是所做的).(同时工作 - 线程回来做其他事情).
但看看这段代码:
public async Task ProcessAsync()
{
Task<string> workTask = SimulateWork();
string st= await workTask;
//do something with st
}
public Task <string> SimulateWork()
{
return ...
}
Run Code Online (Sandbox Code Playgroud)
当线程到达时await workTask;,将方法拆分为2个部分.所以在SimulateWork完成之后 - 方法的继续:AKA://do something with st- 被执行.
一切都好
但是,如果方法是:
public async Task ProcessAsync()
{
Task<string> workTask = SimulateWork();
await workTask; //i …Run Code Online (Sandbox Code Playgroud) 我有以下场景,我认为这可能很常见:
有一个任务(一个UI命令处理程序)可以同步或异步完成.
命令的到达速度可能比处理它们的速度快.
如果命令已有待处理任务,则应对新命令处理程序任务进行排队并按顺序处理.
每个新任务的结果可能取决于前一个任务的结果.
应该遵守取消,但为了简单起见,我想将其排除在本问题的范围之外.此外,线程安全(并发)不是必需的,但必须支持重入.
这是我想要实现的基本示例(作为控制台应用程序,为简单起见):
using System;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var asyncOp = new AsyncOp<int>();
Func<int, Task<int>> handleAsync = async (arg) =>
{
Console.WriteLine("this task arg: " + arg);
//await Task.Delay(arg); // make it async
return await Task.FromResult(arg); // sync
};
Console.WriteLine("Test #1...");
asyncOp.RunAsync(() => handleAsync(1000));
asyncOp.RunAsync(() => handleAsync(900));
asyncOp.RunAsync(() => handleAsync(800));
asyncOp.CurrentTask.Wait();
Console.WriteLine("\nPress any key to continue to test #2...");
Console.ReadLine();
asyncOp.RunAsync(() =>
{
asyncOp.RunAsync(() => handleAsync(200)); …Run Code Online (Sandbox Code Playgroud) 更新,我现在已经尝试解释我所看到的行为,但从可靠的来源获得关于行为的答案仍然很棒unhandledRejection。我还在Reddit 上发起了一个讨论。
为什么我unhandledRejection在下面的代码中得到一个事件(对于“错误 f1”)?这是出乎意料的,因为我在处理这两个拒绝finally的部分main。
我在 Node (v14.13.1) 和 Chrome (v86.0.4240.75) 中看到了相同的行为:
window.addEventListener("unhandledrejection", event => {
console.warn(`unhandledRejection: ${event.reason.message}`);
});
function delay(ms) {
return new Promise(r => setTimeout(r, ms));
}
async function f1() {
await delay(100);
throw new Error("error f1");
}
async function f2() {
await delay(200);
throw new Error("error f2");
}
async function main() {
// start all at once
const [p1, p2] = [f1(), f2()]; …Run Code Online (Sandbox Code Playgroud)从方法抛出的异常的正常行为async Task是保持休眠状态,直到稍后观察它们,或者直到任务被垃圾收集.
我可以想到我可能想立即投掷的情况.这是一个例子:
public static async Task TestExAsync(string filename)
{
// the file is missing, but it may be there again
// when the exception gets observed 5 seconds later,
// hard to debug
if (!System.IO.File.Exists(filename))
throw new System.IO.FileNotFoundException(filename);
await Task.Delay(1000);
}
public static void Main()
{
var task = TestExAsync("filename");
try
{
Thread.Sleep(5000); // do other work
task.Wait(); // wait and observe
}
catch (AggregateException ex)
{
Console.WriteLine(new { ex.InnerException.Message, task.IsCanceled });
}
Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)
我可以用 …
async-await ×10
c# ×8
.net ×4
asynchronous ×1
c#-5.0 ×1
es6-promise ×1
javascript ×1
node.js ×1
promise ×1
tcplistener ×1
threadpool ×1
unit-testing ×1
xunit ×1