我在我的函数中打开n
个并发线程:
List<string> _files = new List<string>();
public void Start()
{
CancellationTokenSource _tokenSource = new CancellationTokenSource();
var token = _tokenSource.Token;
Task.Factory.StartNew(() =>
{
try
{
Parallel.ForEach(_files,
new ParallelOptions
{
MaxDegreeOfParallelism = 5 //limit number of parallel threads
},
file =>
{
if (token.IsCancellationRequested)
return;
//do work...
});
}
catch (Exception)
{ }
}, _tokenSource.Token).ContinueWith(
t =>
{
//finish...
}
, TaskScheduler.FromCurrentSynchronizationContext() //to ContinueWith (update UI) from UI thread
);
}
线程打开后我注意到它从我的列表中选择了随机文件。
是否可以每次从我的列表中选择第一个n
元素?
答案 0 :(得分:4)
要获得您想要的行为,您需要编写custom partitioner,它看起来“随机”的原因是它现在正在以块的形式批处理文件列表,所以如果您的源列表是
List<string> files = List<string> { "a", "b", "c", "d", "e", "f", "g", "h", "i" };
当它分区时,它可以像这样均匀地分割它(如果Max是3个线程):
因此,如果你看过正在处理的文件,它可能看起来像
"a", "d", "g", "e", "b", "h", "c", "f", "i"
如果你制作一个自定义分区程序,你可以让它一次只取一个项目,而不是一次批处理,使工作清单看起来像
GetTheNextUnprocessedString()
GetTheNextUnprocessedString()
GetTheNextUnprocessedString()
如果您使用的是.NET 4.5,则可以使用this factory,如下所示:
Parallel.ForEach(Partitioner.Create(_files, EnumerablePartitionerOptions.NoBuffering),
new ParallelOptions
{
MaxDegreeOfParallelism = 5 //limit number of parallel threads
},
(file, loopstate, index) =>
{
if (token.IsCancellationRequested)
return;
//do work...
});
如果您不使用.NET 4.5,这不是一项微不足道的任务,所以我不打算在这里为您编写。阅读我在顶部链接的MSDN文章,您最终可以弄明白。
我会做的是问自己“我真的需要按顺序处理文件吗?”如果您不需要它们按顺序让它自己进行排序,因为您通过执行订单可能会做的唯一事情可能会减慢流程。
答案 1 :(得分:3)
如果以特定顺序启动工作项很重要,请不要依赖Parallel.ForEach
;正如其他人所说,你可以根据需要进行配置,但这并不容易。
更容易的选择是创建5个不同的任务来处理项目。它无法根据需要动态添加/删除工作人员,但无论如何你似乎都没有充分利用它。
只需创建一个BlockingCollection
和5个从中获取项目的任务:
var queue = new BlockingCollection<string>();
int workers = 5;
CancellationTokenSource cts = new CancellationTokenSource();
var tasks = new List<Task>();
for (int i = 0; i < workers; i++)
{
tasks.Add(Task.Run(() =>
{
foreach (var item in queue.GetConsumingEnumerable())
{
cts.Token.ThrowIfCancellationRequested();
DoWork(item);
}
}, cts.Token));
}
//throw this into a new task if adding the items will take too long
foreach (var item in data)
queue.Add(item);
queue.CompleteAdding();
Task.WhenAll(tasks).ContinueWith(t =>
{
//do completion stuff
});
答案 2 :(得分:0)
当然,这些文件是随机选择的,这就是parallel.foreach的重点。如果你并行,你指定的5个线程将使用输入,因为它由数据分包者决定。
但是如果您真的想维护订单,请检查您可以为parallel.foreach指定的OrderablePartitioner。 - &GT; http://msdn.microsoft.com/en-us/library/dd989583.aspx 但当然这会降低性能,但它允许您指定如何为线程创建分区。