线程最佳实现

时间:2012-02-19 11:41:56

标签: c# multithreading threadpool

我有一个应用程序,我有很多“搜索”同时运行(搜索需要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​​,所以我不能做异步

6 个答案:

答案 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");
}
相关问题