我有一个应用程序,我有很多“搜索”同时运行(搜索需要1到10秒才能完成,具体取决于可用的结果数量)问题是搜索时的延迟会越来越大(我认为因为25个Max线程)我正在使用Backgroundworker类Atm。所以我查了几个其他的实现:
简单示例:
static void Main()
{
for (int i = 0; i < 500; i++)
{
try
{
new Thread(new ParameterizedThreadStart(doWork)).Start(i);
}
catch { }
}
Console.ReadLine();
}
static void doWork(object i)
{
Console.WriteLine(i + ": started");
Thread.Sleep(1000);
Console.WriteLine(i + " done");
Thread.CurrentThread.Abort();
}
但我得到例外,我放弃了线程(让我担心) 所以我尝试了线程池:
static void Main()
{
for (int i = 0; i < 500; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(doWork), i);
}
Console.ReadLine();
}
static void doWork(object i)
{
Console.WriteLine(i + ": started");
Thread.Sleep(1000);
Console.WriteLine(i + " done");
}
但这很慢......
我仍然在寻找最佳实施方案,任何人都可以帮助我吗?
编辑:DoWork方法建立网络连接(并等待它完成)这是使用API,所以我不能做异步
答案 0 :(得分:1)
如果搜索受CPU限制,我会使用Parallel.For
或并行linq,并手动指定MaxDegreeOfParallelism
。通常,在这种情况下,虚拟核心数是最佳线程数。
如果搜索等待外部事物(例如IO绑定,等待网络上的响应,......),我会查看非阻塞API,因此每次搜索都不需要一个线程。
答案 1 :(得分:1)
试试这个解决方案:
static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
Task t = new Task(doWork, i);
t.Start();
}
Console.ReadLine();
}
static void doWork(object i)
{
Console.WriteLine(i + ": started");
Thread.SpinWait(20000000); // It depends on what doWork actually does whether SpinWait or Sleep is the most appropriate test
//Thread.Sleep(1000);
Console.WriteLine(i + " done");
}
使用任务,您可以更好地控制工作项,并使用可提高性能的选项来增强工作项。任务的默认TaskScheduler
使用ThreadPool
对工作项进行排队。 (阅读本答案的底部以获取有关任务的更多信息。)
所以,回答这个问题我们需要知道doWork
实际上做了什么:-)但总的来说Task
将是一个很好的选择和一个很好的抽象。
并列foreach
如果您使用循环来生成作业,并且您正在执行data parallelism,那么并行foreach可以完成这项工作:
Parallel.For(0, 500, i => doWork(i));
<强>链接:强>
来自消费者的评论
http://msdn.microsoft.com/en-us/library/dd537609.aspx
任务提供两个主要好处:
1)更有效,更可扩展地使用系统资源。
在幕后,任务排队到ThreadPool,一直是 增强了算法(如爬山),确定和 调整到最大化吞吐量的线程数。这使得 任务相对轻量级,你可以创建其中的许多 实现细粒度并行。为了补充这一点,广为人知 工作窃取算法用于提供负载平衡。
2)比线程或工作项更多的程序控制。
任务和围绕它们构建的框架提供了丰富的API 支持等待,取消,延续,强大的例外 处理,详细状态,自定义日程安排等。
更新回答
嗯,遗憾的是它是一个糟糕的API,因为它不允许你异步地执行它。它可能会变慢,因为你同时开始这么多连接(或者你开始的太多)。
试试这个:
var jobs = new[] { 1, 2, 3};
var options = new ParallelOptions { MaxDegreeOfParallelism = 3 };
Parallel.ForEach(jobs, options, i => doWork(i));
尝试MaxDegreeOfParallelism
的价值。
答案 2 :(得分:1)
任何涉及排队500个项目以供ThreadPool处理的内容都不会以最佳吞吐量运行。 ThreadPool通常非常不愿意启动额外的线程,因为这种用法并不是设计者的意图。
听起来像是你的IO绑定,在这种情况下你可以异步执行IO并用很少的线程来处理所有内容。但是,在不了解您的工作量的情况下,这是一个猜测游戏。
答案 3 :(得分:0)
请勿使用Thread.Abort
。对于一个只是想要优雅地结束的糟糕工作威胁来说,这是一个相当暴力的结局。您可以让原始的doWork方法结束,线程将被释放。
关于你的第二个解决方案 - 你在一秒钟内排队500个线程,这比ThreadPool可以同时运行的要多得多。它需要更多的时间,因为它们是一个接一个地运行。
.NET 4.0中的另一个选项是System.Threading.Tasks
中的任务库,它是您可能考虑的基于线程池的更智能的解决方案。
但是要回到原来的问题 - 使用BackgroundWorkers时,“延迟时间越长”究竟是什么意思?你的意思是当有多个搜索进行时,每个单独的搜索需要更长时间(接近10秒)?如果是这样,我会尝试寻找其他地方的瓶颈。什么是“搜索”?你在访问数据库吗?网络连接?也许你在应用程序中导致瓶颈的部分有锁。
答案 4 :(得分:0)
1)如果你调用abort,你会得到一个ThreadAbortException:Thread.Abort
2)¿500线程?我认为你做的事情很糟糕。(除非你正在使用GPU)
答案 5 :(得分:0)
线程池尝试最小化创建的线程数,而不是为排队任务创建新线程,它可以等待池中的其他线程被释放。这样做是有原因的 - 太多线程可能会妨碍性能。但是,您可以在发生此限制之前覆盖要创建的最小线程数。
这是您的原始代码,带有更正以使其快速运行:
static void Main()
{
int minWorker, minIOC;
ThreadPool.GetMinThreads(out minWorker, out minIOC);
ThreadPool.SetMinThreads(50, minIOC);
for (int i = 0; i < 500; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(doWork), i);
}
Console.ReadLine();
}
static void doWork(object i)
{
Console.WriteLine(i + ": started");
Thread.Sleep(1000);
Console.WriteLine(i + " done");
}