=SUM(COUNTIF(INDIRECT({"M3","P3","S3"}),">0"))
上述函数应该启动多个private async Task MainTask(CancellationToken token)
{
List<Task> tasks = new List<Task>();
do
{
var data = StaticVariables.AllData;
foreach (var dataPiece in data)
{
tasks.Add((new Task(() => DoSomething(data))));
}
Parallel.ForEach(tasks, task => task.Start());
await Task.WhenAll(tasks);
tasks.Clear();
await Task.Delay(2000);
} while (!token.IsCancellationRequested);
}
方法并同时运行它们。 DoSomething(task)
在返回DoSomething
之前超时为2秒。经过一些测试,似乎是
false
和
await Task.WhenAll(tasks);
大约需要2秒*的任务。所以看起来他们会这样做:
我怎么能这样做才能同时开始并同时进行操作?
修改
这样做:
tasks.Clear()
导致可怕的性能(完成旧代码大约需要25秒,完成此代码需要115秒)
答案 0 :(得分:2)
您在此处看到的问题是由于线程池维护了准备运行的最小线程数。如果线程池需要创建比该最小线程更多的线程,则在创建每个新线程之间会引入故意的1秒延迟。
这样做是为了防止诸如“线程踩踏”之类的东西淹没系统并同时创建许多线程。
您可以使用the ThreadPool.SetMinThreads() method更改最低线程限制。但是,建议不要这样做,因为它会破坏预期的线程池操作,并可能导致其他进程变慢。
如果你真的必须这样做,这是一个示例控制台应用程序:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static Stopwatch sw = Stopwatch.StartNew();
static void Main()
{
runTasks();
setMinThreadPoolThreads(30);
runTasks();
}
static void setMinThreadPoolThreads(int count)
{
Console.WriteLine("\nSetting min thread pool threads to {0}.\n", count);
int workerThreads, completionPortThreads;
ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
ThreadPool.SetMinThreads(count, completionPortThreads);
}
static void runTasks()
{
var sw = Stopwatch.StartNew();
Console.WriteLine("\nStarting tasks.");
var task = test(20);
Console.WriteLine("Waiting for tasks to finish.");
task.Wait();
Console.WriteLine("Finished after " + sw.Elapsed);
}
static async Task test(int n)
{
var tasks = new List<Task>();
for (int i = 0; i < n; ++i)
tasks.Add(Task.Run(new Action(task)));
await Task.WhenAll(tasks);
}
static void task()
{
Console.WriteLine("Task starting at time " + sw.Elapsed);
Thread.Sleep(5000);
Console.WriteLine("Task stopping at time " + sw.Elapsed);
}
}
}
如果你运行它,你会在输出中看到在设置最小线程池大小之前运行test()
,任务将花费大约10秒(并且你会看到任务开始时间之间的延迟增加在最初的几个任务之后)。
将最小线程池线程设置为30后,新任务启动之间的延迟要短得多,并且运行test()
的总时间下降到大约5秒(在我的PC上 - 你的可能会有所不同!)
但是,我只想重申,设置最小线程池大小不是正常的事情,应该谨慎对待。正如Microsoft文档所说:
默认情况下,最小线程数设置为系统上的处理器数。您可以使用SetMinThreads方法来增加最小线程数。但是,不必要地增加这些值可能会导致性能问题。如果太多任务同时启动,则所有任务可能看起来都很慢。在大多数情况下,线程池将使用自己的算法来分配线程,从而表现更好。将最小值降低到小于处理器数量也会影响性能。
答案 1 :(得分:1)
首先,您应该使用Task.Run
而不是在单独的步骤中创建和启动任务。
你可以在循环或Linq风格中这样做。如果您使用Linq,只需确保您不会遇到延迟执行,其中第二个任务仅在第一个任务完成后才开始。创建所选任务的列表,数组或其他持久集合:
await Task.WhenAll(data.Select(dataPiece => Task.Run(() => DoSomething(dataPiece)).ToList());
另一个问题是DoSomething
的内容。只要这是一个同步方法,它就会阻塞它的执行线程,直到完成为止。对于固有的异步操作(比如ping一些网络地址),重新设计方法可以防止这种线程阻塞行为。
Matthew Watson回答的另一个选择是增加可用线程的数量,因此每个任务都可以在自己的线程中运行。这不是最好的选择,但是如果你有许多任务在没有实际工作的情况下有很长的阻塞时间,那么更多线程将有助于完成工作。
如果任务实际使用可用的物理资源,CPU或IO绑定工作,则更多线程将无法帮助。