简体   繁体   English

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

[英]Need a queue of jobs to be processed by threads

I have some work (a job) that is in a queue (so there a several of them) and I want each job to be processed by a thread.我有一些工作(一份工作)在队列中(所以有几个),我希望每个工作都由一个线程处理。

I was looking at Rx but this is not what I wanted and then came across the parallel task library.我在看 Rx 但这不是我想要的,然后遇到了并行任务库。

Since my work will be done in an web application I do not want client to be waiting for each job to be finished, so I have done the following:由于我的工作将在 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");
    }

I would call the above code on a web request and then the client will not have to wait.我会在 web 请求上调用上述代码,然后客户端就不必等待了。

Is this the correct way to do this?这是正确的方法吗?

TIA TIA

Another way is to use Tasks:另一种方法是使用任务:

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

From the MSDN docs I see that Unitys IContainer Resolve method is not thread safe (or it is not written).从 MSDN 文档中,我看到 Unitys IContainer Resolve 方法不是线程安全的(或者它没有被写入)。 This would mean that you need to do that out of the thread loop.这意味着您需要在线程循环之外执行此操作。 Edit: changed to Task .编辑:更改为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");
}

Plus as fiver stated, if you have.Net 4 then Tasks is the way to go.另外,正如fiver所说,如果你有.Net 4,那么Tasks就是通往go的方式。

Why go Task (question in comment):为什么 go 任务(评论中的问题):

If your method fromClientRequest would be fired insanely often, you would fill the thread pool, and overall system performance would probably not be as good as with.Net 4 with fine graining.如果您的方法 fromClientRequest 经常被疯狂地触发,您将填满线程池,并且整体系统性能可能不如细粒度的 .Net 4 好。 This is where Task enters the game.这是Task进入游戏的地方。 Each task is not its own thread but the new.Net 4 thread pool creates enough threads to maximize performance on a system, and you do not need to bother on how many cpus and how much thread context switches would there be.每个任务都不是自己的线程,但新的 .Net 4 线程池创建了足够的线程以最大限度地提高系统性能,您无需担心会有多少 cpu 和多少线程上下文切换。

Some MSDN quotes for ThreadPool: ThreadPool 的一些 MSDN 引用:

When all thread pool threads have been assigned to tasks, the thread pool does not immediately begin creating new idle threads.当所有线程池线程都已分配给任务时,线程池不会立即开始创建新的空闲线程。 To avoid unnecessarily allocating stack space for threads, it creates new idle threads at intervals.为了避免不必要地为线程分配堆栈空间,它会每隔一段时间创建新的空闲线程。 The interval is currently half a second, although it could change in future versions of the .NET Framework.该时间间隔目前为半秒,但在 .NET 框架的未来版本中可能会发生变化。

The thread pool has a default size of 250 worker threads per available processor线程池的默认大小为每个可用处理器 250 个工作线程

Unnecessarily increasing the number of idle threads can also cause performance problems.不必要地增加空闲线程的数量也会导致性能问题。 Stack space must be allocated for each thread.必须为每个线程分配堆栈空间。 If too many tasks start at the same time, all of them might appear to be slow.如果同时启动太多任务,所有任务都可能看起来很慢。 Finding the right balance is a performance-tuning issue.找到正确的平衡是一个性能调整问题。

By using Tasks you discard those issues.通过使用任务,您可以丢弃这些问题。

Another good thing is you can fine grain the type of operation to run.另一个好处是您可以细化运行的操作类型。 This is important if your tasks do run blocking operations.如果您的任务确实运行阻塞操作,这一点很重要。 This is a case where more threads are to be allocated concurrently since they would mostly wait.这是一种需要同时分配更多线程的情况,因为它们大多会等待。 ThreadPool cannot achieve this automagically: ThreadPool 无法自动实现这一点:

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

And of course you are able to make it finish on demand without resorting to ManualResetEvent:当然,您无需借助 ManualResetEvent 就可以按需完成:

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

Beside this you don't have to change the Parallel.ForEach if you don't expect exceptions or blocking, since it is part of the.Net 4 Task Parallel Library, and (often) works well and optimized on the.Net 4 pool as Tasks do.除此之外,如果您不希望出现异常或阻塞,则不必更改 Parallel.ForEach,因为它是 .Net 4 任务并行库的一部分,并且(通常)在 .Net 4 池上运行良好并进行了优化就像任务一样。

However if you do go to Tasks instead of parallel for, remove the LongRunning from the caller Task, since Parallel.For is a blocking operations and Starting tasks (with the fiver loop) is not.但是,如果您对 Tasks 执行 go 而不是 parallel for,请从调用者 Task 中删除 LongRunning,因为 Parallel.For 是一个阻塞操作,而 Starting tasks (with the Fiver loop) 不是。 But this way you loose the kinda first-come-first-served optimization, or you have to do it on a lot more Tasks (all spawned through ids) which probably would give less correct behaviour.但是这样你就失去了先到先服务的优化,或者你必须在更多的任务(全部通过 ids 产生)上执行它,这可能会给出不太正确的行为。 Another option is to wait on all tasks at the end of DoSomeWorkInParallel.另一种选择是在 DoSomeWorkInParallel 结束时等待所有任务。

I would call the above code on a web request and then the client will not have to wait.我会在 web 请求上调用上述代码,然后客户端就不必等待了。

This will work provided the client does not need an answer (like Ok/Fail).如果客户不需要答案(如 Ok/Fail),这将起作用。

Is this the correct way to do this?这是正确的方法吗?

Almost.几乎。 You use Parallel.ForEach (TPL) for the jobs but run it from a 'plain' Threadpool job.您对作业使用 Parallel.ForEach (TPL),但从“普通”线程池作业运行它。 Better to use a Task for the outer job as well.最好将任务也用于外部工作。

Also, handle all exceptions in that outer Task.此外,处理该外部任务中的所有异常。 And be careful about the thread-safety of the container etc.并注意容器的线程安全等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM