这是正确的实施吗?

时间:2014-06-24 07:12:28

标签: c# task-parallel-library long-running-processes

我的Windows服务需要从数据库中选择作业并需要处理它。

在这里,每项工作都是一个扫描过程,大约需要10分钟才能完成。

我对任务并行库很新。我已按以下方式实现了示例逻辑:

Queue queue = new Queue();

for (int i = 0; i < 10000; i++)
{
    queue.Enqueue(i);
}

for (int i = 0; i < 100; i++)
{
    Task.Factory.StartNew((Object data ) =>
    {
        var Objdata = (Queue)data;
        Console.WriteLine(Objdata.Dequeue());
        Console.WriteLine(
            "The current thread is " + Thread.CurrentThread.ManagedThreadId);
    }, queue, TaskCreationOptions.LongRunning);
}

Console.ReadLine();

但是,这创造了很多线程。 由于循环重复100次,因此创建了100个线程。

创建多个并行线程是正确的方法吗?

有没有办法将线程数限制为10(并发级别)?

2 个答案:

答案 0 :(得分:3)

分配新Threads时要记住的一个重要因素是操作系统必须分配许多逻辑实体才能运行当前线程:

  1. 线程内核对象 - 用于描述线程的对象, 包括线程的上下文,cpu寄存器等
  2. 线程环境块 - 用于异常处理和线程本地 存储
  3. 用户模式堆栈 - 1MB堆栈
  4. 内核模式堆栈 - 用于将参数从用户模式传递到内核 模式
  5. 除此之外,可能运行的并发Threads的数量取决于您的计算机正在打包的核心数,并且创建的线程数量大于您的计算机拥有的核心数量将开始导致Context Switching,从长远来看,这可能会减慢您的工作速度。

    经过长时间的介绍,到了好东西。我们实际想要做的是限制运行的线程数,并尽可能多地重用它们。

    对于这种工作,我会选择基于Producer-Consumer模式的TPL Dataflow。只是一个可以做的事情的小例子:

    // a BufferBlock is an equivalent of a ConcurrentQueue to buffer your objects
    var bufferBlock = new BufferBlock<object>();
    
    // An ActionBlock to process each object and do something with it
    var actionBlock = new ActionBlock<object>(obj =>
    {
         // Do stuff with the objects from the bufferblock
    });
    
    bufferBlock.LinkTo(actionBlock);
    bufferBlock.Completion.ContinueWith(t => actionBlock.Complete());
    

    您可以传递每个Block一个ExecutionDataflowBlockOptions,这可能会限制Bounded Capacity(BufferBlock中的对象数量)和MaxDegreeOfParallelism,它会告诉块最大数量你可能想要的并发性。

    有一个很好的例子here可以帮助你入门。

答案 1 :(得分:2)

很高兴你问,因为从某种意义上说你是对的 - 这不是最好的方法。

Task的概念不应与Thread混淆。可以将Thread与厨房中的厨师进行比较,而Task是客户订购的菜肴。你有一堆厨师,他们按照一些顺序处理菜单(通常是FIFO)。厨师完成一道菜然后继续前进。线程池的概念是相同的。您创建了一堆要完成的任务,但您不需要为每个任务分配新线程。

好的实际位是这样的。有几个。第一个是ThreadPoll.QueueUserWorkItem。 (http://msdn.microsoft.com/en-us/library/system.threading.threadpool.queueuserworkitem(v=vs.110).aspx)。使用并行库,也可以使用Parallel.For,它将根据系统中可用的实际CPU核心数自动生成线程。

Parallel.For(0, 100, i=>{
    //here, this method will be called 100 times, and i will be 0 to 100
    WaitForGrassToGrow();
    Console.WriteLine(string.Format("The {0}-th task has completed!",i));
});

请注意,无法保证Parallel.For调用的方法按顺序调用(0,1,2,3,4,5 ...)。实际的顺序取决于执行。