【发布时间】:2018-11-24 09:04:00
【问题描述】:
我一直在努力寻找解决此问题的最佳方法。我已经在谷歌上搜索了几个小时,并且发现了一些好的资源(其中大部分都在这里),但我仍然无法弄清楚解决我的问题的最佳方法是什么。我发现的大多数提示要么没有提到 ConfigureAwait(false),要么只是在存储任务执行以供以后使用时具有相同的方法签名。
我要做的是首先确定在任务中运行什么方法。然后我想运行其他一些任务,然后确定任务。我编辑了下面的代码以使其更清晰。
在我的库中,我有以下场景(非常简化):
public abstract class BaseClass
{
public int Foo;
}
public class ClassA : BaseClass { }
public class ClassB : BaseClass
{
public int Bar;
}
public async Task ProcessVariable(int variable)
{
BaseClass c = null; // initialized by child class
Task t = null;
switch (variable)
{
case 1:
c = new ClassA(variable); // parse data for ClassA and BaseClass
// potentially some special code here
t = OnClassA(c as ClassA, "foo");
break;
case 2:
c = new ClassB(variable); // parse data for ClassB and BaseClass
// potentially some special code here
t = OnClassB(c as ClassB);
break;
}
// first call different task than the one picked in switch
// can't call it before switch, as variable c is initialized in the switch body
if (c != null)
await OnAnyClass(c).ConfigureAwait(false);
// finally, call the async task we picked inside switch cases
if (t != null)
await t.ConfigureAwait(false);
}
public virtual async Task OnAnyClass(BaseClass c)
{
// this one does something
await SendAsync(c.Foo).ConfigureAwait(false);
}
public virtual async Task OnClassA(ClassA c, string additionalInfo)
{
// this one does nothing, but end user can override
await Task.CompletedTask;
}
public virtual async Task OnClassB(ClassB c)
{
// this one does something
await SendAsync(c.Bar).ConfigureAwait(false);
}
这通常效果很好。但是,OnClassA 和 OnClassB 在 OnAnyClass 之前执行。当然,这是预期的行为,因为该方法会立即执行并返回 Task。但是,我希望 OnAnyClass 在其他类之前执行。我在谷歌上搜索了很多关于如何解决这个问题的提示。我遇到了几个解决方案,但我不确定哪一个最适合我的场景:
1.使用任务构造函数
t = new Task(() => OnClassB(c as ClassB));
// and then
t.Start();
await t.ConfigureAwait(false);
但是,通过这种方式,我不确定代码的行为方式。我见过几个例子,但最后我只是感到困惑。我是否仍能从 ConfigureAwait(false) 中获得全部好处(这对我来说很重要,因为它是一个库代码),并且执行方法中的所有等待都会被异步正确调用吗?
2。使用带有异步委托的 Task 构造函数
t = new Task(async () => await OnClassB(c as ClassB).ConfigureAwait(false));
// and then
t.Start();
await t.ConfigureAwait(false);
现在,通过这种方法,我知道 ConfigureAwait(false) 将被正确使用。但是,这会将 Task 包装在 Task 中 - 由于库的用户将能够覆盖这些方法,我认为这并不是真正可取的。
3.使用 Func
这种方法会很棒 - 但是,每个方法都有不同的签名,我想保持这种方式,所以如果不让代码比现在更混乱,这对我来说真的不起作用。
4.等待案例中的方法
case 1:
c = new ClassA(variable);
await OnAnyClass(c).ConfigureAwait(false);
await OnClassA(c as ClassA, "foo").ConfigureAwait(false);
break;
这种方法保证有效。但是,由于我的库的性质,可能有几十种情况 - 因此这会导致代码可维护性降低和大量重复的代码行。
5.等待最后的所有方法
case 1:
c = new ClassA(variable);
break;
// and then
if (c != null)
await OnAnyClass(c).ConfigureAwait(false);
if (c is ClassA)
await OnClassA(c as ClassA, "foo").ConfigureAwait(false);
if (c is ClassB)
await OnClassB(c as ClassB).ConfigureAwait(false);
这也肯定会起作用,并且与方法 4 相比会减少重复行的数量,但是此代码维护起来仍然非常烦人。
在最坏的情况下,我可能会保持原样,忽略顺序 - 在大多数情况下,它应该无关紧要。但是,如果可能的话,我宁愿保留订单。哪种方法效果最好?由于它是一个库代码,我需要 ConfigureAwait(false) 才能正确执行。你有什么意见和建议?希望我以一种可以理解的方式解释了我的问题。
提前谢谢你,如果我遗漏了一些明显的东西,我深表歉意 - 我使用线程多年,而 async/await 对我来说相对较新。
【问题讨论】:
-
TestClass 未定义
-
@paparazzo 我跳过了它,因为它不相关 - 它可以是任何东西,我只是展示了构造函数使用一些数据来初始化它们自己和基类。
-
如果不相关,则传递一个字符串。
-
@paparazzo 我在生产代码中执行 - 然后将字符串解析为 JToken,并根据 Json 值,切换执行不同的情况。不相关是指它与我遇到的问题无关。不过,为了简单起见,我将其编辑为 int。
-
与您无关。不完整的代码会得到不完整的评论。为什么不努力发布完整的代码?
标签: c# asynchronous async-await c#-7.1