我创建了一个多任务程序。该程序有大约20个主要任务,每个任务调用一些子任务来操作文件I / O.我希望每个主要任务每500ms定期重复一次,所以我输入了代码Task.Delay(500)。
问题是Task.Delay有时会延迟超过500ms。有一种情况,它延迟超过3秒。 我该如何解决?
原始程序非常大,我在下面创建了一个示例程序。 (1)如果Task.Delay打开,则发生过度延迟。 (2)如果Thead.Sleep打开,则不会发生过度延迟。
ThreadPool.SetMinThreads()似乎无法解决它。
感谢。
class Program
{
const int DELAY_TIME = 500;
const int TASKS = 100;
const int WAITS = 100;
const int WARNING_THRESHOLD = 100;
static void Main(string[] args)
{
//ThreadPool.SetMinThreads(workerThreads: 200, completionPortThreads: 200);
Console.WriteLine("*** Start...");
Test();
Console.WriteLine("*** Done!");
Console.ReadKey();
}
private static void Test()
{
List<Task> tasks = new List<Task>();
for (int taskId = 0; taskId < TASKS; taskId++)
{
tasks.Add(DelaysAsync(taskId));
}
Task.WaitAll(tasks.ToArray());
}
static async Task DelaysAsync(int taskId)
{
await Task.Yield();
Stopwatch sw = new Stopwatch();
for (int i = 0; i < WAITS; i++)
{
sw.Reset();
sw.Start();
await Task.Delay(DELAY_TIME).ConfigureAwait(false); // (1)
//Thread.Sleep(DELAY_TIME); // (2)
sw.Stop();
Console.Write($"Task({taskId})_iter({i}) Elapsed={sw.ElapsedMilliseconds}");
if (sw.ElapsedMilliseconds > DELAY_TIME + WARNING_THRESHOLD)
{
Console.WriteLine(" *********** Too late!! ************");
}
else
{
Console.WriteLine();
}
}
}
}
答案 0 :(得分:1)
我使用.NET 4.6.1和VS 2017进行测试。在Xeon E3-1230 v3 CPU中,它从未打印过“太晚”,经过的值在498-527 ms之内。
Thread.Sleep版本执行非常相似,每次睡眠500-528ms,但总执行时间要长得多,因为运行时拒绝创建100个OS线程,这太多了,所以少于100个DelaysAsync函数并行运行。调试器显示我在Thread.Sleep版本中有27个工作线程,在Task.Delay版本中只有9个工作线程。
我认为您的PC上还有其他应用程序会创建太多线程并占用过多CPU。 Windows尝试均匀地平衡线程,因此当整个系统受CPU限制时,更多的本机线程=更多的CPU时间,因此抖动更少。
如果是这种情况,并且您希望在调度程序中确定应用程序的优先级,而不是使用Thread.Sleep和更多线程,raise the priority您的进程。
答案 1 :(得分:0)
似乎我能找到答案。我改变了以前的示例程序,如下所示。主要区别在于使用StopWatch或DateTime来测量持续时间。 在StopWatch版本中,会发生许多延迟。 在DateTime版本中,没有或至少发生很少的延迟。
我猜原因是StopWatch和Task.Delay都使用了Timer的争用。我总结说我不应该一起使用StopWatch和Task.Delay。
谢谢。
class Program
{
const int DELAY_TIME = 500;
const int TASKS = 100;
const int WAITS = 100;
const int WARNING_THRESHOLD = 500;
static void Main(string[] args)
{
using (Process p = Process.GetCurrentProcess())
{
p.PriorityClass = ProcessPriorityClass.RealTime;
//ThreadPool.SetMinThreads(workerThreads: 200, completionPortThreads: 200);
int workerThreads;
int completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Console.WriteLine($"{workerThreads}, {completionPortThreads}");
Console.WriteLine("*** Start...");
Test();
Console.WriteLine("*** Done!");
Console.ReadKey();
}
}
private static void Test()
{
int totalCount = 0;
List<Task<int>> tasks = new List<Task<int>>();
for (int taskId = 0; taskId < TASKS; taskId++)
{
//tasks.Add(DelaysWithStopWatchAsync(taskId)); // many delays
tasks.Add(DelaysWithDateTimeAsync(taskId)); // no delays
}
Task.WaitAll(tasks.ToArray());
foreach (var task in tasks)
{
totalCount += task.Result;
}
Console.WriteLine($"Total counts of deday = {totalCount}");
}
static async Task<int> DelaysWithStopWatchAsync(int taskId)
{
await Task.Yield();
int count = 0;
Stopwatch sw = new Stopwatch();
for (int i = 0; i < WAITS; i++)
{
sw.Reset();
sw.Start();
await Task.Delay(DELAY_TIME).ConfigureAwait(false); // (1)
//Thread.Sleep(DELAY_TIME); // (2)
sw.Stop();
Console.Write($"task({taskId})_iter({i}) elapsed={sw.ElapsedMilliseconds}");
if (sw.ElapsedMilliseconds > DELAY_TIME + WARNING_THRESHOLD)
{
Console.WriteLine($" *********** Too late!! ************");
count++;
}
else
{
Console.WriteLine();
}
}
return count;
}
static async Task<int> DelaysWithDateTimeAsync(int taskId)
{
await Task.Yield();
int count = 0;
for (int i = 0; i < WAITS; i++)
{
DateTime start = DateTime.Now;
await Task.Delay(DELAY_TIME).ConfigureAwait(false); // (1)
//Thread.Sleep(DELAY_TIME); // (2)
DateTime end = DateTime.Now;
int duration = (end - start).Milliseconds;
Console.Write($"Task({taskId})_iter({i}) Elapsed={duration}");
if (duration > DELAY_TIME + WARNING_THRESHOLD)
{
Console.WriteLine($" *********** Too late!! ************");
count++;
}
else
{
Console.WriteLine();
}
}
return count;
}
}