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

本文关键字:作业队列 处理 线程 | 更新日期: 2023-09-27 18:00:54

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

我在看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

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

从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,那么Tasks就是你的选择。

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

如果您的方法fromClientRequest经常被疯狂地激发,那么您将填充线程池,并且总体系统性能可能不如具有细粒度的.Net 4。这就是任务进入游戏的地方。每个任务都不是自己的线程,但新的.Net 4线程池创建了足够的线程来最大限度地提高系统性能,并且您不需要担心会有多少cpu和多少线程上下文切换。

ThreadPool:的一些MSDN报价

当所有线程池线程分配给任务,线程池不会立即开始创建新的空闲线程。避免不必要地分配堆栈空间对于线程,它会创建新的空闲线程间隔。间隔为目前是半秒,尽管可能在未来版本中更改.NET框架。

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

不必要地增加空闲线程也可能导致性能问题。堆栈空间必须为每个线程分配。如果也是许多任务同时开始,所有其中一些可能看起来很慢。找到正确的平衡是性能调整问题。

使用"任务"可以丢弃这些问题。

另一个好处是,您可以细化要运行的操作类型。如果您的任务确实运行了阻塞操作,这一点非常重要。这是一种同时分配更多线程的情况,因为它们大部分时间都在等待。ThreadPool无法自动实现这一点:

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

当然,您可以根据需要完成,而无需求助于手动重置事件:

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

除此之外,如果您不希望出现异常或阻塞,则无需更改Parallel.ForEach,因为它是.Net 4任务并行库的一部分,并且(通常(与任务一样在.Net 4池中运行良好并进行了优化。

但是,如果您确实转到Tasks而不是parallel for,请从调用方Task中删除LongRunning,因为parallel.for是一个阻塞操作,而Starting Tasks(使用fiver循环(不是。但通过这种方式,你可以放松那种先到先得的优化,或者你必须在更多的任务(都是通过id产生的(上进行优化,这可能会导致不太正确的行为。另一种选择是在DoSomeWorkInParallel结束时等待所有任务。

另一种方法是使用Tasks:

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

我会在网上调用上面的代码请求,然后客户端不会必须等待。

如果客户不需要回答(如"确定"/"失败"(,这将起作用。

这是正确的吗怎么做?

差不多。您对作业使用Parallel.ForEach(TPL(,但从"普通"线程池作业运行它。最好也将Task用于外部工作。

此外,还要处理该外部任务中的所有异常。注意容器等的螺纹安全。