Task.Factory.StartNew<TResult; 和 Task.Run<TResult; 到底有什么区别?
Posted MyIO
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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>>
:
这是为什么呢?
原理
其实是因为上述代码传入的参数类型不是 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 hasnt 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“
以上是关于Task.Factory.StartNew<TResult; 和 Task.Run<TResult; 到底有什么区别?的主要内容,如果未能解决你的问题,请参考以下文章
带有异步 lambda 和 Task.WaitAll 的 Task.Factory.StartNew
Parallel.ForEach 与 Task.Factory.StartNew
Task.Run 和Task.Factory.StartNew 区别