我正试图围绕整个并行编程概念,主要关注任务,所以我一直在尝试这种情况,例如,最多9个并行任务将在一段随机的时间内完成他们的工作:
/// <remarks>
/// A big thank you to the awesome community of StackOverflow for
/// their advice and guidance with this sample project
/// http://stackoverflow.com/questions/5195486/
/// </remarks>
RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
byte[] buffer = new byte[4];
random.GetBytes(buffer);
// Creates a random number of tasks (not less than 2 -- up to 9)
int iteration = new Random(BitConverter.ToInt32(buffer, 0)).Next(2,9);
Console.WriteLine("Creating " + iteration + " Parallel Tasks . . .");
Console.Write(Environment.NewLine);
Dictionary<int, string> items = new Dictionary<int, string>();
for (int i = 1; i < iteration + 1; i++) // cosmetic +1 to avoid "Task N° 0"
{
items.Add(i, "Task # " + i);
}
List<Task> tasks = new List<Task>();
// I guess we should use a Parallel.Foreach() here
foreach (var item in items)
{
// Creates a random interval to pause the thread at (up to 9 secs)
random.GetBytes(buffer);
int interval = new Random(BitConverter.ToInt32(buffer, 0)).Next(1000, 9000);
var temp = item;
var task = Task.Factory.StartNew(state =>
{
Console.WriteLine(String.Format(temp.Value + " will be completed in {0} miliseconds . . .", interval));
Thread.Sleep(interval);
return "The quick brown fox jumps over the lazy dog.";
}, temp.Value).ContinueWith(t => Console.WriteLine(String.Format("{0} returned: {1}", t.AsyncState, t.Result)));
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
但不幸的是,它们是按顺序处理而不是并行处理。
如果你们能帮助我,我真的很高兴 - 也许我应该使用Parallel.ForEach而不是常规的?
再次提出任何建议都会非常感激。
修改 更新了代码示例两次以反映评论者的贡献。
答案 0 :(得分:4)
您正在循环中的每个任务上调用task.Result
...这意味着它将在创建下一个任务之前等待一个任务的结果。试试这个:
// Create all the tasks
foreach (var item in items)
{
// ... Create and start tasks as before
tasks.Add(task);
}
// Now wait for them all, printing the results
foreach (var task in tasks)
{
Console.WriteLine(task.AsyncState + " returned: " + task.Result);
}
现在这将阻止立即创建的第一个任务 - 所以即使(比方说)第5个任务更早完成,在第1个完成之前你不会看到任何结果......但它们将并行执行。
接下来你要改变你的循环:
foreach (var item in items)
{
var itemCopy = item;
// Use itemCopy in here instead of item
}
否则你正在捕获循环变量,这被认为是有害的(part 1; part 2)。
但是,您可能 应该使用Parallel.ForEach
。值得理解为什么它之前失败了。
答案 1 :(得分:1)
调用Task.Result
块。由于您在foreach
项目中执行此操作,因此最终创建一项任务,然后等待其完成,然后再转到下一项目。
尝试将呼叫转移到Task.Result
foreach
List<Task<string>> tasks = new List<Task<string>>();
foreach (var item in items)
{
random.GetBytes(buffer);
int interval = new Random(BitConverter.ToInt32(buffer, 0)).Next(1000, 9000);
var task = Task.Factory.StartNew(state =>
{
Console.WriteLine(String.Format(item.Value + " will be completed in {0} miliseconds . . .", interval));
Thread.Sleep(interval);
return "The quick brown fox jumps over the lazy dog.";
}, item.Value);
tasks.Add(task);
}
foreach (var task in tasks)
{
Console.WriteLine(task.AsyncState + " returned: " + task.Result);
}
修改强>
正如评论中所要求的那样。这是一个版本,可以在每个任务完成后使用ConinueWith
打印结果。任务列表现在也可以再次为List<Task>
。最后仍需要进行WaitAll
调用,以确保方法在每个任务完成之前不会返回,但每个任务都会在完成后打印出结果。
RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
byte[] buffer = new byte[4];
random.GetBytes(buffer);
// Creates a random number of tasks (not less than 2 -- up to 9)
int iteration = new Random(BitConverter.ToInt32(buffer, 0)).Next(2, 9);
Console.WriteLine("Creating " + iteration + " Parallel Tasks . . .");
Dictionary<int, string> items = new Dictionary<int, string>();
for (int i = 1; i < iteration + 1; i++) // cosmetic +1 to avoid "Task N° 0"
{
items.Add(i, "Parallel Task N° " + i);
}
List<Task> tasks = new List<Task>();
// I guess we should use a Parallel.Foreach() here
foreach (var item in items)
{
// Creates a random interval to pause the thread at (up to 9 secs)
random.GetBytes(buffer);
int interval = new Random(BitConverter.ToInt32(buffer, 0)).Next(1000, 9000);
// http://stackoverflow.com/questions/5195486/
var temp = item;
var task = Task.Factory.StartNew(state =>
{
Console.WriteLine(String.Format(temp.Value + " will be completed in {0} miliseconds . . .", interval));
Thread.Sleep(interval);
return "The quick brown fox jumps over the lazy dog.";
}, temp.Value).ContinueWith(t => Console.WriteLine(t.AsyncState + " returned: " + t.Result));
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
答案 2 :(得分:1)
由于您在每个任务上调用task.Result
,因此您正在创建一个阻止调用,导致循环等待该任务返回。
您应首先创建所有任务,然后在单独的循环中读取结果。