问题
I want that the method executes tasks in parallel and when the task is finished "yield return" the result. Is it possible to have something like that :
public IEnumerable<string> GetAllLogs()
{
var computers = GetComputers()
.Where(cpt => cpt.IsOnline);
Parallel.ForEach(computers, c => c.GetLogs());
// How to 'yield return' ?
}
Thx !!!
EDIT :
Maybe my previous sample was not enough explicit here a new and (I hope) more explicit one ;-)
I want to know how to parallize the GetAllLogs method :
public class ComputerManager
{
public IEnumerable<string> GetAllLogs(IEnumerable<IComputer> computers)
{
foreach (var cpt in computers)
{
// How to Parallelize the foreach bloc and
// use a 'yield return' to keep the IEnumerable<string> return type ?
foreach (var log in cpt.GetLogs())
{
yield return log;
}
}
}
}
static void Main(string[] args)
{
ComputerManager cm = new ComputerManager();
IComputer[] computers = new IComputer[] {new Computer(), new Computer2(), new Computer3(), new Computer4() };
Stopwatch sw = new Stopwatch();
sw.Start();
foreach (string s in cm.GetAllLogs(computers))
{
Console.WriteLine(s);
}
sw.Stop();
Console.WriteLine("Terminé en : {0}" , sw.ElapsedMilliseconds);
Console.Read();
}
}
public interface IComputer
{
IEnumerable<string> GetLogs();
}
public class Computer : IComputer
{
public IEnumerable<string> GetLogs()
{
string[] alphabet = new string[] { "a", "b", "c", "d", "e"};
foreach (var letter in alphabet)
{
Thread.Sleep(1000);
yield return letter;
}
}
}
public class Computer2 : IComputer
{
public IEnumerable<string> GetLogs()
{
string[] figures = new string[] { "1", "2", "3", "4", "5" };
foreach (var figure in figures)
{
Thread.Sleep(1000);
yield return figure;
}
}
}
public class Computer3 : IComputer
{
public IEnumerable<string> GetLogs()
{
string[] greekAlphabet = new string[] { "alpha", "beta", "gamma", "delta", "epsilon" };
foreach (var letter in greekAlphabet)
{
Thread.Sleep(1000);
yield return letter;
}
}
}
public class Computer4 : IComputer
{
public IEnumerable<string> GetLogs()
{
string[] romanFigures = new string[] { "I", "II", "III", "IV", "V" };
foreach (var s in romanFigures)
{
Thread.Sleep(1000);
yield return s;
}
}
}
回答1:
Reed Copsey (User from SO) posted this at the MSDN forums. This might help!
http://social.msdn.microsoft.com/Forums/en-AU/parallelextensions/thread/3352a322-af6f-4105-b25c-9978bd85f162
// In your source, you use yield
public ClassImplementingIEnumerable: IEnumerable<int>
{
public IEnumerable<int> GetSource()
{
for (int i=0;i<1000;++i)
yield return i;
}
}
public class ParallelProcessingConsumer {
public void SomeMethod(ClassImplementingIEnumerable sourceProvider)
{
Parallel.ForEach(sourceProvider.GetSource(), parallelOptions, (i, loopState) =>
{
// Do work in parallel!
});
}
回答2:
If you want to return as soon as the parallel execution begins :
public class ComputerManager
{
public IEnumerable<string> GetAllLogs(IEnumerable<IComputer> computers)
{
return computers.AsParallel().SelectMany(c => c.GetLogs());
}
}
If you want to return when the parallel execution finished :
public class ComputerManager
{
public IEnumerable<string> GetAllLogs(IEnumerable<IComputer> computers)
{
return computers.AsParallel().SelectMany(c => c.GetLogs()).ToArray();
}
}
回答3:
This works for me:
- In a Task in a Parallel.Foreach Enqueue in a ConcurrentQueue the item transformed to be processed.
- The task has a continue that marks a flag with that task ends.
- In the same thread of execution with tasks ends a while dequeue and yields
Fast and excellent results for me:
Task.Factory.StartNew (() =>
{
Parallel.ForEach<string> (TextHelper.ReadLines(FileName), ProcessHelper.DefaultParallelOptions,
(string currentLine) =>
{
// Read line, validate and enqeue to an instance of FileLineData (custom class)
});
}).
ContinueWith
(
ic => isCompleted = true
);
while (!isCompleted || qlines.Count > 0)
{
if (qlines.TryDequeue (out returnLine))
{
yield return returnLine;
}
}
来源:https://stackoverflow.com/questions/8254780/is-it-possible-to-have-a-method-using-parallel-tasks-and-returns-ienumerablet