需要一个由线程处理的作业队列

时间:2011-07-03 07:44:25

标签: c# multithreading

我有一些工作(作业)在队列中(所以有几个),我希望每个作业都由一个线程处理。

我在看Rx,但这不是我想要的,然后遇到并行任务库。

由于我的工作将在Web应用程序中完成,我不希望客户端等待每个工作完成,所以我做了以下工作:

    public void FromWebClientRequest(int[] ids);
    {
        // I will get the objects for the ids from a repository using a container (UNITY)


       ThreadPool.QueueUserWorkItem(delegate
                                         {
                                             DoSomeWorkInParallel(ids, container);
                                         });
    }

    private static void DoSomeWorkInParallel(int[] ids, container)
    {
        Parallel.ForEach(ids, id=>
                                        {
                                            Some work will be done here...
                                            var respository = container.Resolve...
                                        });


       // Here all the work will be done.
       container.Resolve<ILogger>().Log("finished all work");
    }

我会在网络请求中调用上面的代码,然后客户端就不必等了。

这是正确的方法吗?

TIA

3 个答案:

答案 0 :(得分:3)

另一种方法是使用任务:

public static void FromWebClientRequest(int[] ids)
{
    foreach (var id in ids)
    {
        Task.Factory.StartNew(i =>
        {
            Wl(i);
        }
        , id);
    }
}

答案 1 :(得分:3)

从MSDN文档中我看到Unitys IContainer Resolve方法不是线程安全的(或者它没有写入)。这意味着您需要在线程循环中执行此操作。修改:更改为Task

public void FromWebClientRequest(int[] ids);
{
   IRepoType repoType = container.Resolve<IRepoType>();
   ILogger logger = container.Resolve<ILogger>();
   // remove LongRunning if your operations are not blocking (Ie. read file or download file  long running queries etc)
   // prefer fairness is here to try to complete first the requests that came first, so client are more likely to be able to be served "first come, first served" in case of high CPU use with lot of requests
   Task.Factory.StartNew(() => DoSomeWorkInParallel(ids, repoType, logger), TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
}

private static void DoSomeWorkInParallel(int[] ids, IRepoType repository, ILogger logger)
{
    // if there are blocking operations inside this loop you ought to convert it to tasks with LongRunning
    // why this? to force more threads as usually would be used to run the loop, and try to saturate cpu use, which would be doing nothing most of the time
    // beware of doing this if you work on a non clustered database, since you can saturate it and have a bottleneck there, you should try and see how it handles your workload
    Parallel.ForEach(ids, id=>{
                  // Some work will be done here...
                  // use repository
             });
   logger.Log("finished all work");
}

正如fiver所说,如果你有.Net 4,那么任务就是你要走的路。

为什么要去任务(评论中的问题):

如果您的方法fromClientRequest经常被疯狂地解雇,那么您将填充线程池,整体系统性能可能不如使用精细粒度的.Net 4那样好。这是Task进入游戏的地方。每个任务都不是它自己的线程,但是新的.Net 4线程池创建了足够的线程来最大化系统的性能,而且你不需要打扰有多少cpus以及有多少线程上下文切换。

ThreadPool的一些MSDN引用:

  

当所有线程池线程都已存在时   分配给任务,线程池   没有立即开始创建   新的空闲线程。避免   不必要地分配堆栈空间   对于线程,它会创建新的空闲   线程间隔。间隔是   目前半秒钟,虽然它   可能会在以后的版本中改变   .NET Framework。

     

线程池的默认大小为   每个可用250个工作线程   处理器

     

不必要地增加数量   空闲线程也可能导致   性能问题。堆栈空间必须   为每个线程分配。如果是的话   所有人都同时开始了许多任务   他们似乎很慢。   找到适当的平衡是一个   性能调整问题。

通过使用任务,您可以放弃这些问题。

另一个好处是你可以细粒度运行的操作类型。如果您的任务执行阻止操作,这一点很重要。这是一种同时分配更多线程的情况,因为它们通常会等待。 ThreadPool无法自动实现:

Task.Factory.StartNew(() => DoSomeWork(), TaskCreationOptions.LongRunning);

当然,您可以按需完成,而无需使用ManualResetEvent:

var task = Task.Factory.StartNew(() => DoSomeWork());
task.Wait();

除此之外,如果您不期望异常或阻塞,则不必更改Parallel.ForEach,因为它是.Net 4任务并行库的一部分,并且(通常)运行良好并且在。任务执行的Net 4池。

但是,如果您确实转到Tasks而不是并行,则从调用者Task中删除LongRunning,因为Parallel.For是一个阻塞操作,而起始任务(使用fiver循环)则不是。但是这样你就失去了先到先得的优化,或者你必须在更多的任务(所有通过id产生)上做这件事,这可能会给出不太正确的行为。另一种选择是在DoSomeWorkInParallel结束时等待所有任务。

答案 2 :(得分:1)

  

我会在网上调用上面的代码   请求然后客户端不会   不得不等

如果客户端不需要答案(如Ok / Fail),这将有效。

  

这是正确的吗?   这样做的方法?

几乎。您可以将Parallel.ForEach(TPL)用于作业,但可以从“普通”Threadpool作业运行它。最好还为外部工作使用Task。

此外,处理该外部任务中的所有异常。并注意容器等的线程安全性。