线程最佳实现
本文关键字:实现 最佳 线程 | 更新日期: 2023-09-27 18:25:01
我有一个应用程序,我有很多"搜索"同时运行(搜索需要1-10秒才能完成,具体取决于可用的结果数量)问题是搜索时的延迟越来越大(我认为是因为最多有25个线程)我使用的是Backgroundworker class 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,因此我无法执行异步
如果搜索是CPU绑定的,我将使用Parallel.For
或并行linq,并手动指定MaxDegreeOfParallelism
。在这种情况下,虚拟核心的数量通常是线程的最佳数量。
如果搜索正在等待外部的东西(例如IO绑定,等待网络上的响应,…),我会考虑非阻塞API,所以每次搜索不需要一个线程。
任何涉及排队500个项目以供ThreadPool处理的事情都不会以最佳吞吐量运行。ThreadPool通常不愿意增加额外的线程,因为这种用法不是设计者想要的。
在我看来,这听起来像是绑定了IO,在这种情况下,您可以异步执行IO,并用很少的线程为所有内容提供服务。然而,在不了解更多工作量的情况下,这有点像一场猜谜游戏。
尝试此解决方案:
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
如果你使用循环来生成作业,并且你正在进行数据并行,那么并行foreach就可以完成这项工作:
Parallel.For(0, 500, i => doWork(i));
链接:
- http://msdn.microsoft.com/en-us/library/dd537609.aspx
- http://msdn.microsoft.com/en-us/library/dd460717.aspx
致消费者的评论
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));
并对CCD_ 8值进行了实验。
线程池试图最大限度地减少创建的线程数量,而不是为排队的任务创建新线程,它可以等待池中的其他线程被释放。它这样做是有原因的——太多的线程可能会影响性能。但是,您可以覆盖在此限制发生之前要创建的最小线程数。
以下是您的原始代码,并进行了更正以使其快速工作:
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");
}
不要使用Thread.Abort
。这是对一个只想优雅结束的糟糕工作威胁的一个相当暴力的结束。你可以让原始的doWork方法结束,线程就会被释放。
关于您的第二个解决方案,您将在一秒钟内排队500个线程,这远远超过ThreadPool可以并发运行的数量。这需要更多的时间,因为他们一个接一个地跑。
.NET 4.0中的另一个选项是System.Threading.Tasks
中的任务库,这是您可能会考虑的一个更智能的基于线程池的解决方案。
但回到你最初的问题——当使用BackgroundWorkers时,你所说的"延迟变得更长"到底是什么意思?你的意思是,当有多个搜索正在进行时,每个单独的搜索需要更长的时间(接近10秒)吗?如果是这样的话,我会尝试在其他地方寻找瓶颈。什么是"搜索"?您正在访问数据库吗?网络连接?也许您在应用程序的那个部分中有导致瓶颈的锁定。
1)如果您调用中止,则会得到ThreadAbortException:Thread.Artrt
2) ?500个螺纹?我认为你做的事情很糟糕。(除非您使用GPU)