简体   繁体   English

此代码是否应返回任务或任务 <object> ?

[英]Should this code return a Task or Task<object>?

I was reading The Nature of TaskCompletionSource , a post by Stephen Toub. 我正在阅读The Task of TaskCompletionSource ,这是Stephen Toub的一篇文章。

public static Task RunAsync(Action action)
{
    var tcs = new TaskCompletionSource<Object>();
    ThreadPool.QueueUserWorkItem(_ =>
    {
        try
        {
            action();
            tcs.SetResult(null);
        }
        catch(Exception exc) { tcs.SetException(exc); }
    });
    return tcs.Task;
}

Since we no longer care what the type of T is, I've defaulted to using Object . 由于我们不再关心T的类型,我默认使用Object Then, when the Action is executed successfully, SetResult is still used to transition the Task into the RanToCompletion final state; 然后,当Action成功执行时, SetResult仍然用于将Task转换为RanToCompletion最终状态; however, since the actual result value is irrelevant, null is used. 但是,由于实际结果值无关紧要,因此使用null Finally, RunAsync returns Task rather than Task<Object> . 最后, RunAsync返回Task而不是Task<Object> Of course, the instantiated task 's type is still Task<Object> , but we need not refer to it as such, and the consumer of this method need not care about those implementation details. 当然,实例化的task的类型仍然是Task<Object> ,但是我们不需要这样引用它,并且该方法的使用者不需要关心那些实现细节。

I don't particularly understand why the method should return Task rather than Task<object> (which is why I emphasised the bold sentence). 我不是特别理解为什么该方法应该返回Task而不是Task<object> (这就是我强调粗体句子的原因)。 I know the method is set to return Task but tcs is a TaskCompletionSource<Object> , not TaskCompletionSource (which is wrong, I think). 我知道该方法设置为返回Task但是tcsTaskCompletionSource<Object> ,而不是TaskCompletionSource (我认为这是错误的)。

There isn't a non generic TaskCompletionSource and considering all you want is a task without a result, the result doesn't matter. 没有非通用的TaskCompletionSource并且考虑到你想要的只是一个没有结果的任务,结果无关紧要。 The caller doesn't know and doesn't care in this case that the Task is actually a Task<object> , The caller just await s it, and gets an exception if there is one. 在这种情况下,调用者不知道也不关心Task实际上是一个Task<object> ,调用者只是await它,并且如果有的话就会获得异常。 The caller is unaware of the actual result. 呼叫者不知道实际结果。

This of course is facilitated by the fact that Task<T> inherits from Task 这当然是由Task<T>继承自Task的事实所促成的


It's also common to find a Task<bool> that returns false, or Task<int> with 0. 找到返回false的Task<bool>或使用0返回Task<int>也很常见。

There is no non-generic TaskCompletionSource class for creating instances of Task which are not instances of Task<T> . 没有用于创建Task实例的非通用TaskCompletionSource类,它们不是Task<T>实例。 This leaves two options for the generic type parameter for TaskCompletionSource<T> when you don't care about (or are not providing) the return value: 当您不关心(或不提供)返回值时,这为TaskCompletionSource<T>的泛型类型参数留下了两个选项:

  1. Use an arbitrary existing type, such as object , as the return type. 使用任意现有类型(如object )作为返回类型。 Set the value to null to indicate completion of the task. 将值设置为null以指示任务完成。
  2. Use a specific non-public type, and set the value to null to indicate completion of the task. 使用特定的非公共类型,并将值设置为null以指示任务的完成。

When I create a TaskCompletionSource<T> instance for the purpose of providing a Task with no return value, I prefer to use a dedicated non-public type to ensure consuming code will not mistake the returned Task as an instance of Task<T> where the result has meaning. 当我创建一个TaskCompletionSource<T>实例以提供没有返回值的Task时,我更喜欢使用专用的非公共类型来确保使用代码不会将返回的Task误认为Task<T>的实例结果有意义。

First, I define the following class (it can be a private sealed class if it's nested within another type): 首先,我定义了以下类(如果嵌套在另一个类型中,它可以是private sealed class ):

internal sealed class VoidResult
{
}

Then, instead of using TaskCompletionSource<object> for the completion source, I use TaskCompletionSource<VoidResult> . 然后,我使用TaskCompletionSource<VoidResult>而不是使用TaskCompletionSource<object>作为完成源。 Since the VoidResult type is not accessible by calling code, the user will be unable to cast the Task object to an instance of Task<VoidResult> . 由于无法通过调用代码访问VoidResult类型,因此用户将无法将Task对象Task<VoidResult>Task<VoidResult>的实例。

I don't particularly understand why the method should return Task rather than Task<object> 我不特别理解为什么该方法应该返回Task而不是Task<object>

Because when you return Task<Object> it means that when this method completes it will produce some useful value of type Object . 因为当您返回Task<Object>这意味着当此方法完成时,它将生成一些Object类型的有用值。 in this case we're not producing any result, That's why stephen choose to return Task . 在这种情况下,我们没有产生任何结果,这就是为什么stephen选择返回Task

If we're dealing with Func<Object> then returning Task<Object> would be appropriate, as Func will produce some result, we may choose to return it. 如果我们处理Func<Object>然后返回Task<Object>是合适的,因为Func会产生一些结果,我们可能会选择返回它。

Why TaskCompletionSource<Object> , not TaskCompletionSource ? 为什么TaskCompletionSource<Object> ,而不是TaskCompletionSource

Because there's no such thing. 因为没有这样的事情。 There is no non generic TaskCompletionSource . 没有非通用的TaskCompletionSource

If you returned a Task<object> , then var result = await RunAsync(...) would always return null , since that's what you're setting the result to. 如果您返回了Task<object> ,则var result = await RunAsync(...)将始终返回null ,因为这是您要将结果设置为。

The client doesn't care about this, so you just return a Task . 客户端不关心这个,所以你只需要返回一个Task

Ideally, you would use a TaskCompletionSource internally, instead of a TaskCompletionSource<object> , and just call something like SetCompleted() instead of SetResult(null) . 理想情况下,您可以在内部使用TaskCompletionSource ,而不是使用TaskCompletionSource<object> ,只需调用SetCompleted()而不是SetResult(null) But such type does not exist. 但这种类型不存在。

如何返回任务<object>在子任务上使用延续<div id="text_translate"><p>我喜欢尽量减少相互等待但不显示它的任务的调度 我将如何更改我的 TestOption1 以返回调用方法的任务?</p><p> <a href="https://i.stack.imgur.com/ua5wo.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ua5wo.png" alt="任务继续"></a></p><pre> [TestClass()] public class SqlServerTests { public const string Membership = "Data Source=LocalHost;Initial Catalog=tempdb;Integrated Security=True;"; [TestMethod()] public async Task ContinueWithTest() { using CancellationTokenSource cts = new CancellationTokenSource(); //warm up so pooling is enabled on all 3 methods using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "set nocount on"; con.Open(); cmd.ExecuteNonQuery(); } var sw2 = System.Diagnostics.Stopwatch.StartNew(); await TestOption2(cts.Token).ConfigureAwait(false); sw2.Stop(); //allow the benefit of the doubt for the slower and give it cashed plans var sw3 = System.Diagnostics.Stopwatch.StartNew(); await TestOption3(cts.Token).ConfigureAwait(false); sw3.Stop(); Assert.IsTrue(sw2.ElapsedTicks &lt; sw3.ElapsedTicks, "Stopwatch 2 {0} Stopwatch 3 {1}", sw2, sw3); var sw1 = System.Diagnostics.Stopwatch.StartNew(); await TestOption1(cts.Token).ConfigureAwait(false); sw1.Stop(); Console.WriteLine($"TestOption1: No internal awaits {sw1.ElapsedTicks:N0} ticks"); Console.WriteLine($"TestOption2: 1x internal await {sw2.ElapsedTicks:N0} ticks"); Console.WriteLine($"TestOption3: 2x internal await {sw3.ElapsedTicks:N0} ticks"); Assert.IsTrue(sw1.ElapsedTicks &lt; sw2.ElapsedTicks, "Stopwatch 1 {0} Stopwatch 2 {1}", sw1, sw2); Assert.IsTrue(sw1.ElapsedTicks &lt; sw3.ElapsedTicks, "Stopwatch 1 {0} Stopwatch 3 {1}", sw1, sw3); } private static Task TestOption1(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "set nocount on"; return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open.... .ContinueWith((t) =&gt; cmd.ExecuteNonQuery(), cancellationToken, continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default); } } private static async Task TestOption2(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "set nocount on"; await con.OpenAsync(cancellationToken).ContinueWith((_) =&gt; cmd.ExecuteNonQuery(), cancellationToken).ConfigureAwait(false); } } private static async Task TestOption3(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "set nocount on"; await con.OpenAsync(cancellationToken).ConfigureAwait(false); await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); } } }</pre><p> 我希望能够做这样的事情</p><pre> [TestMethod] public async Task TestContinueWithDelegate() { var data = await TestOptionReturn().ConfigureAwait(false); Assert.IsNotNull(data); } private static Task&lt;object&gt; TestOptionReturn(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.StoredProcedure; cmd.CommandText = "Test1"; return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open.... .ContinueWith(delegate { return cmd.ExecuteScalar(); }, cancellationToken, continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default); } }</pre><p> 由于数据库未打开,以下测试失败<a href="https://i.stack.imgur.com/fa7DO.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/fa7DO.png" alt="在此处输入图像描述"></a></p><p> [TestMethod] public async Task TestContinueWithDelegate() { using CancellationTokenSource cts = new CancellationTokenSource(); var 数据 = 等待 TestOptioDDL(cts.Token).ConfigureAwait(false); 使用 var reader = await TestOptionOutput(cts.Token).ConfigureAwait(false); 断言.IsNotNull(数据); 断言.IsTrue(reader.Read()); }</p><pre> private static Task&lt;object&gt; TestOptioDDL(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.StoredProcedure; cmd.CommandText = "TestOutput"; cmd.Parameters.Add(new SqlParameter("Data", System.Data.SqlDbType.DateTime) { IsNullable = true, Direction = System.Data.ParameterDirection.Output }); return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open.... .ContinueWith(delegate { cmd.ExecuteScalar(); return cmd.Parameters[0].Value; }, cancellationToken, continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default); } } private static Task&lt;SqlDataReader&gt; TestOptionOutput(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "select * from sys.databases"; cmd.Parameters.Add(new SqlParameter("Data", System.Data.SqlDbType.DateTime) { IsNullable = true, Direction = System.Data.ParameterDirection.Output }); return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open.... .ContinueWith(delegate { return cmd.ExecuteReader(); }, cancellationToken, continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default); } }</pre></div></object> - How to return Task<Object> using continuation on a child task

暂无
暂无

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

相关问题 存储库是否应返回Task <SomeEntity> ? - Should a repository return Task<SomeEntity>? Task对象的退货声明在哪里? - Where is the return statment for the Task object? 应该返回Task的API中的方法是否以Task或Async结尾 - Should methods in an API that return Task end with Task or Async 我应该将任务包装在另一个任务中还是应该返回创建的任务? - Should I wrap a task in another task or should I just return the created task? 我为什么要返回任务<iactionresult>在 Controller 中?</iactionresult> - Why should I return Task<IActionResult> in a Controller? 返回 Task 的方法应该抛出异常吗? - Should methods that return Task throw exceptions? 如何返回任务<object>在子任务上使用延续<div id="text_translate"><p>我喜欢尽量减少相互等待但不显示它的任务的调度 我将如何更改我的 TestOption1 以返回调用方法的任务?</p><p> <a href="https://i.stack.imgur.com/ua5wo.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ua5wo.png" alt="任务继续"></a></p><pre> [TestClass()] public class SqlServerTests { public const string Membership = "Data Source=LocalHost;Initial Catalog=tempdb;Integrated Security=True;"; [TestMethod()] public async Task ContinueWithTest() { using CancellationTokenSource cts = new CancellationTokenSource(); //warm up so pooling is enabled on all 3 methods using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "set nocount on"; con.Open(); cmd.ExecuteNonQuery(); } var sw2 = System.Diagnostics.Stopwatch.StartNew(); await TestOption2(cts.Token).ConfigureAwait(false); sw2.Stop(); //allow the benefit of the doubt for the slower and give it cashed plans var sw3 = System.Diagnostics.Stopwatch.StartNew(); await TestOption3(cts.Token).ConfigureAwait(false); sw3.Stop(); Assert.IsTrue(sw2.ElapsedTicks &lt; sw3.ElapsedTicks, "Stopwatch 2 {0} Stopwatch 3 {1}", sw2, sw3); var sw1 = System.Diagnostics.Stopwatch.StartNew(); await TestOption1(cts.Token).ConfigureAwait(false); sw1.Stop(); Console.WriteLine($"TestOption1: No internal awaits {sw1.ElapsedTicks:N0} ticks"); Console.WriteLine($"TestOption2: 1x internal await {sw2.ElapsedTicks:N0} ticks"); Console.WriteLine($"TestOption3: 2x internal await {sw3.ElapsedTicks:N0} ticks"); Assert.IsTrue(sw1.ElapsedTicks &lt; sw2.ElapsedTicks, "Stopwatch 1 {0} Stopwatch 2 {1}", sw1, sw2); Assert.IsTrue(sw1.ElapsedTicks &lt; sw3.ElapsedTicks, "Stopwatch 1 {0} Stopwatch 3 {1}", sw1, sw3); } private static Task TestOption1(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "set nocount on"; return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open.... .ContinueWith((t) =&gt; cmd.ExecuteNonQuery(), cancellationToken, continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default); } } private static async Task TestOption2(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "set nocount on"; await con.OpenAsync(cancellationToken).ContinueWith((_) =&gt; cmd.ExecuteNonQuery(), cancellationToken).ConfigureAwait(false); } } private static async Task TestOption3(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "set nocount on"; await con.OpenAsync(cancellationToken).ConfigureAwait(false); await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); } } }</pre><p> 我希望能够做这样的事情</p><pre> [TestMethod] public async Task TestContinueWithDelegate() { var data = await TestOptionReturn().ConfigureAwait(false); Assert.IsNotNull(data); } private static Task&lt;object&gt; TestOptionReturn(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.StoredProcedure; cmd.CommandText = "Test1"; return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open.... .ContinueWith(delegate { return cmd.ExecuteScalar(); }, cancellationToken, continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default); } }</pre><p> 由于数据库未打开,以下测试失败<a href="https://i.stack.imgur.com/fa7DO.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/fa7DO.png" alt="在此处输入图像描述"></a></p><p> [TestMethod] public async Task TestContinueWithDelegate() { using CancellationTokenSource cts = new CancellationTokenSource(); var 数据 = 等待 TestOptioDDL(cts.Token).ConfigureAwait(false); 使用 var reader = await TestOptionOutput(cts.Token).ConfigureAwait(false); 断言.IsNotNull(数据); 断言.IsTrue(reader.Read()); }</p><pre> private static Task&lt;object&gt; TestOptioDDL(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.StoredProcedure; cmd.CommandText = "TestOutput"; cmd.Parameters.Add(new SqlParameter("Data", System.Data.SqlDbType.DateTime) { IsNullable = true, Direction = System.Data.ParameterDirection.Output }); return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open.... .ContinueWith(delegate { cmd.ExecuteScalar(); return cmd.Parameters[0].Value; }, cancellationToken, continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default); } } private static Task&lt;SqlDataReader&gt; TestOptionOutput(CancellationToken cancellationToken = default) { using (var con = new SqlConnection(Membership)) using (var cmd = con.CreateCommand()) { cmd.CommandType = System.Data.CommandType.Text; cmd.CommandText = "select * from sys.databases"; cmd.Parameters.Add(new SqlParameter("Data", System.Data.SqlDbType.DateTime) { IsNullable = true, Direction = System.Data.ParameterDirection.Output }); return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open.... .ContinueWith(delegate { return cmd.ExecuteReader(); }, cancellationToken, continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default); } }</pre></div></object> - How to return Task<Object> using continuation on a child task (等待)任务,并非所有代码路径都返回值 - (awaitable) task, not all code paths return a value 在任务主体内的 catch 块中返回一个对象 - Return an object in catch block within Task body 并非所有代码路径都在 Task 上返回值 - Not all code paths return a value on Task
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM