我有一个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(并发级别)?
分配新的Threads
时要记住的一个重要因素是,操作系统必须分配一些逻辑实体,以便当前线程运行:
- 线程内核对象 -用于描述线程的对象;包括线程的上下文,cpu寄存器等
- 线程环境块 -用于异常处理和线程本地存储
- 用户模式栈 - 1MB栈
- 内核模式堆栈 -用于从用户模式传递参数到内核模式
除此之外,可能运行的并发Threads
的数量取决于您的机器正在封装的内核数量,并且创建的线程数量大于您的机器拥有的内核数量将开始导致Context Switching
,这从长远来看可能会减慢您的工作速度。
在长时间的介绍之后,来看看精彩的内容。我们真正想做的是限制运行的线程数量,并尽可能地重用它们。
对于这种工作,我会使用基于Producer-Consumer
模式的TPL数据流。这只是一个可以做什么的小例子:
// 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
,它告诉块你可能想要的最大并发数。
这里有一个很好的例子可以让你开始
很高兴你的问题,因为你是对的-这不是最好的方法。
不应将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…)调用。实际的顺序取决于执行。