Task.Factory.StartNew<TResult> 和 Task.Run<TResult> 到底有什么区别?

前言

这不是和《Task.Factory.StartNew 和 Task.Run 到底有什么区别?》一样吗,怎么又写一篇?

起先我也是这么觉得的,但实际发现并非如此。

实现代码

查看这 2 个方法的内部实现,其内部实现逻辑其实是一样的,只是传的默认参数不同:

//Task.Factory.StartNew<TResult>
public Task<TResult> StartNew<TResult>(Func<TResult> function)
{
    Task? currTask = Task.InternalCurrent;
    return Task<TResult>.StartNew(currTask, function, m_defaultCancellationToken,
        m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask));
}

//Task.Run<TResult>
public static Task<TResult> Run<TResult>(Func<TResult> function)
{
    return Task<TResult>.StartNew(null, function, default,
        TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default);
}

这不还和上次一样吗?

Demo

让我们创建代码验证一下:

Stopwatch stopwatch1 = new Stopwatch();
stopwatch1.Start();
var task1 = Task.Factory.StartNew(async () =>
{
    await Task.Delay(1000);

    return "Task.Factory.StartNew";
});

Console.WriteLine(await task1);
stopwatch1.Stop();
Console.WriteLine(stopwatch1.ElapsedMilliseconds);

Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
stopwatch2.Start();
var task2 = Task.Run(async () =>
{
    await Task.Delay(1000);
    return "Task.Run";
});

Console.WriteLine(await task2);
stopwatch2.Stop();
Console.WriteLine(stopwatch2.ElapsedMilliseconds);

运行程序,你将会看到类似的如下输出:

System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[System.String,ConsoleApp1.Program+<>c+<<Main>b__0_0>d]
86
Task.Run
1024

是不是很你想的结果完全不一样!

使用 Task.Factory.StartNew<TResult> 的返回并不是 Task<string>,而是Task<Task<string>>:

03499c9d5293e861aef186ac782889fd.png

这是为什么呢?

原理

其实是因为上述代码传入的参数类型不是 Func<TResult> 而是 Func<Task<TResult>?>,而 Task.Run<Task<TResult>?> 对此做了一层封装:

public static Task<TResult> Run<TResult>(Func<Task<TResult>?> function, CancellationToken cancellationToken)
{
    ...

    Task<Task<TResult>?> task1 = Task<Task<TResult>?>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

    UnwrapPromise<TResult> promise = new UnwrapPromise<TResult>(task1, lookForOce: true);

    return promise;
}

内部同样使用 Task.Factory.StartNew<Task<TResult>?> 生成任务,但是返回的是 UnwrapPromise<TResult>:

// This class encapsulates all "unwrap" logic, and also implements ITaskCompletionAction,
// which minimizes the allocations needed for queuing it to its antecedent.  This
// logic is used by both the Unwrap extension methods and the unwrap-style Task.Run methods.
internal sealed class UnwrapPromise<TResult> : Task<TResult>, ITaskCompletionAction

Task.Factory.StartNew<TResult> 没有这层封装。

不过,要想Task.Factory.StartNew<TResult>达到Task.Run<TResult>同样目的,可以使用 Unwrap 方法:

public static Task<TResult> Unwrap<TResult>(this Task<Task<TResult>> task!!) =>
    // If the task hasn't completed or was faulted/canceled, wrap it in an unwrap promise. Otherwise,
    // it completed successfully.  Return its inner task to avoid unnecessary wrapping, or if the inner
    // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise.
    !task.IsCompletedSuccessfully ? Task.CreateUnwrapPromise<TResult>(task, lookForOce: false) :
    task.Result ??
    Task.FromCanceled<TResult>(new CancellationToken(true));

//使用示例
var task1 = Task.Factory.StartNew(async () =>
{
    await Task.Delay(1000);

    return "Task.Factory.StartNew";
});

Console.WriteLine(await task1.Unwrap());

结论

在使用 Task.Factory.StartNew 时,如果需要等待内部任务的最终完成,需要使用 Unwrap 方法进行“解开”。

想了解更多内容,请关注我的个人公众号”My IO“

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值