Kr0*_*r0e 10 c# mono multithreading async-await .net-4.5
下面的代码应该(至少在我看来)创建100 Tasks,它们都在并行等待(这是关于并发的点,右边:D?)并且几乎同时完成.我想,每Task.Delay一个Timer对象在内部创建.
public static async Task MainAsync() {
var tasks = new List<Task>();
for (var i = 0; i < 100; i++) {
Func<Task> func = async () => {
await Task.Delay(1000);
Console.WriteLine("Instant");
};
tasks.Add(func());
}
await Task.WhenAll(tasks);
}
public static void Main(string[] args) {
MainAsync().Wait();
}
Run Code Online (Sandbox Code Playgroud)
但!当我在Mono上运行时,我会得到非常奇怪的行为:
Tasks不会在同一时间完成,存在着巨大的延迟(大概在500-600ms)加载程序集:/Users/xxxxx/Programming/xxxxx/xxxxxxxxxx/bin/Release/xxxxx.exe
线程开始:#2
线程开始:#3
线程开始:#4
线程开始:#5
线程开始:#6
线程开始:#7
线程完成:#3 < - 显然延迟1000ms完成了?
线程完成:#2 < - 显然延迟1000ms完成了?
线程开始:#8
线程开始:#9
线程开始:#10
线程开始:#11
线程开始:#12
线程开始:#13
... 你懂了.
这实际上是一个错误吗?或者我使用错误的库?
[编辑]我使用Timer测试了一个自定义睡眠方法:
public static async Task MainAsync() {
Console.WriteLine("Started");
var tasks = new List<Task>();
for (var i = 0; i < 100; i++) {
Func<Task> func = async () => {
await SleepFast(1000);
Console.WriteLine("Instant");
};
tasks.Add(func());
}
await Task.WhenAll(tasks);
Console.WriteLine("Ready");
}
public static Task SleepFast(int amount) {
var source = new TaskCompletionSource<object>();
new Timer(state => {
var oldSrc = (TaskCompletionSource<object>)state;
oldSrc.SetResult(null);
}, source, amount, 0);
return source.Task;
}
Run Code Online (Sandbox Code Playgroud)
这一次,所有任务都是即时完成的.所以,我认为这是一个非常糟糕的实现或错误.
[Edit2]仅供参考:我Task.Delay现在使用Windows 8.1在.NET上测试了原始代码(使用),它按预期运行(1000 Tasks,并行等待1秒并完成).
所以答案是:Mono的impl.(某些)方法并不完美.一般来说Task.Delay,不启动线程,甚至很多线程都不应该创建多个线程.
在 .NET Framework 桌面上。
简而言之,有一个特殊的 VM 线程,它定期检查计时器队列并在线程池队列上运行计时器的委托。Task.Delay 不会创建新线程,但仍然可能很重,并且不能保证执行顺序或精确的截止日期。据我了解,传递取消Task.Delay可能最终只是从集合中删除项目,而没有线程池工作排队。
通过创建新的System.Threading.Timer将Task.Delay安排为DelayPromise。所有计时器都存储在 AppDomain 的TimerQueue单例中。本机 VM 计时器用于回调 .NET 以检查是否需要从队列中触发任何计时器。计时器委托计划通过ThreadPool.UnsafeQueueUserWorkItem执行 。
从性能角度来看,如果延迟提前结束,取消延迟似乎更好:
open System.Threading
open System.Threading.Tasks
// takes 0.8% CPU
while true do
Thread.Sleep(10)
Task.Delay(50)
// takes 0.4% CPU
let mutable a = new CancellationTokenSource()
while true do
Thread.Sleep(10)
a.Cancel()
a.Dispose()
a <- new CancellationTokenSource()
let token = a.Token
Task.Delay(50,token)
Run Code Online (Sandbox Code Playgroud)
该Task库的设计更多是为了管理阻塞任务而不阻塞整个工作流程(任务异步,微软混淆地称为“任务并行”),而不是为了进行大块并发计算(并行执行)。
任务库使用调度程序并对准备执行的作业进行排队。当作业运行时,它们将在线程池线程上执行,并且这些线程的数量非常有限。有逻辑可以扩展线程数,但除非您有数百个 CPU 核心,否则它将保持较低的数字。
因此,为了回答这个问题,您的一些任务正在排队等待池中的线程,而其他延迟任务已由调度程序发出。
调度程序和线程池逻辑可以在运行时更改,但如果您试图快速完成大量计算,Task则不适合这项工作。如果您想处理大量缓慢的资源(例如磁盘、数据库或互联网资源),Task可能有助于保持应用程序的响应能力。
如果您只是想了解Task尝试这些:
| 归档时间: |
|
| 查看次数: |
4429 次 |
| 最近记录: |