能否请您在这里帮助我了解两种方法之间的区别:
1)为什么第一个不使用线程池而第二个不使用线程池?
2)为什么第二个为您提供可伸缩性,但第一个却不提供-这与线程池有关吗?
3)我怎样才能在总体上调用这两种方法以使它们的目的更明显(更好地突出它们的区别)?
4)我知道可以通过await调用异步Task方法,像这些方法那样缺少异步的Task呢?
public class _12_MyClass
{
public Task SleepAsyncA(int millisecondsTimeout)
{
return Task.Run(() => Thread.Sleep(millisecondsTimeout));
}
public Task SleepAsyncB(int millisecondsTimeout)
{
TaskCompletionSource<bool> tcs = null;
var t = new Timer(delegate { tcs.TrySetResult(true); }, null, -1, -1);
tcs = new TaskCompletionSource<bool>(t);
t.Change(millisecondsTimeout, -1);
return tcs.Task;
}
public static void Main()
{
}
}
答案 0 :(得分:0)
这个问题很难回答,因为它比较了苹果和橙子,声称它们是水果,并试图将它们相互比较。
1)为什么第一个不使用线程池,第二个不使用线程池?**
2)为什么第二个为您提供可伸缩性,但第一个却没有- 这与线程池有关吗?
默认情况下,诸如Task
和Task<TResult>
之类的 TPL 类型使用线程池线程来运行任务。 SleepAsyncA
启动一个任务,然后调用Thread.Sleep
,此操作什么都不做,只是在一段时间内阻塞了当前线程。结果,您通过窃取线程池线程并将其阻塞直到完成才成为计时器。显然,如果保持这种设计规模不变,线程池线程就会用光。
SleepAsyncB
在计时器上工作。 System.Threading.Timer
在线程池线程上调度回调,而不是每次都创建一个新线程。在这种情况下,它只是暂时在TaskCompletionSource
上设置结果。
TaskCompletionSource<TResult>
类型有两个相关用途, 它是创建任务的来源,而该任务的来源 完成。TaskCompletionSource<TResult>
充当生产者Task<TResult>
及其完成。与创建的任务不同Task.Run
等,由Task
没有任何预定的委托 与之相关联,但是提供了允许您控制的方法 任务的寿命和完成情况。
基本上,您正在采用TaskCompletionSource<TResult>
产生的事件模式,并使用Timer
使它像TaskCompletionSouce
一样工作。 timout不会阻塞任何线程,因此更具伸缩性。
3)我如何才能调用main中的两个方法以使其更有效 明显(更好地突出了它们之间的差异)?
给出
Task
用法
public static string DebugInfo
{
get
{
ThreadPool.GetMaxThreads(out var maxThreads, out _);
ThreadPool.GetAvailableThreads(out var threads, out _);
var usedThreads = maxThreads - threads;
var mt = $"{usedThreads.ToString().PadLeft(4)}/{maxThreads.ToString().PadLeft(4)}";
return $"Threads {mt.PadRight(8)}";
}
}
public static Task SleepAsyncA(int millisecondsTimeout)
{
return Task.Run(() => { Console.WriteLine("SleepAsyncA " + DebugInfo); Thread.Sleep(millisecondsTimeout); });
}
public static Task SleepAsyncB(int millisecondsTimeout)
{
TaskCompletionSource<bool> tcs = null;
var t = new Timer(delegate { tcs.TrySetResult(true); }, null, -1, -1);
Console.WriteLine("SleepAsyncB " + DebugInfo);
tcs = new TaskCompletionSource<bool>(t);
t.Change(millisecondsTimeout, -1);
return tcs.Task;
}
输出
var ms = 5000;
Console.WriteLine("Start " + DebugInfo);
var list = Enumerable.Range(0, 10).Select(x => SleepAsyncA(ms));
Task.WaitAll(list.ToArray());
var list2 = Enumerable.Range(0, 10).Select(x => SleepAsyncB(ms));
Task.WaitAll(list2.ToArray());
4)我知道可以通过await调用异步Task方法,那又如何呢? 像这些方法那样缺少异步的任务?
当然!它是避免多余状态机并为您提供效率的常用模式。但是,每次调用Start Threads 0/2047
SleepAsyncA Threads 8/2047
SleepAsyncA Threads 8/2047
SleepAsyncA Threads 8/2047
SleepAsyncA Threads 8/2047
SleepAsyncA Threads 8/2047
SleepAsyncA Threads 8/2047
SleepAsyncA Threads 8/2047
SleepAsyncA Threads 8/2047
SleepAsyncA Threads 9/2047
SleepAsyncA Threads 10/2047
SleepAsyncB Threads 0/2047
SleepAsyncB Threads 0/2047
SleepAsyncB Threads 0/2047
SleepAsyncB Threads 0/2047
SleepAsyncB Threads 0/2047
SleepAsyncB Threads 0/2047
SleepAsyncB Threads 0/2047
SleepAsyncB Threads 0/2047
SleepAsyncB Threads 0/2047
SleepAsyncB Threads 0/2047
时,您的方法都必须标记为await
async
最后,为什么不保存所有麻烦和多余的代码,而只使用private static async Task Main(string[] args)
{
var ms = 5000;
await SleepAsyncA(ms);
await SleepAsyncB(ms);
}
Task.Delay(ms)
可扩展,简单,大惊小怪。