Параллельные задачи обрабатываются как последовательные - PullRequest
1 голос
/ 04 марта 2011

Я пытаюсь обернуть голову вокруг всей концепции параллельного программирования, в основном фокусируясь на задачах, поэтому я пробовал этот сценарий, где, скажем, до 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 вместо обычного?

Опять любой совет был бы очень признателен.

EDIT Обновлен пример кода дважды, чтобы отразить вклад комментаторов.

Ответы [ 3 ]

4 голосов
/ 04 марта 2011

Вы вызываете 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
}

иначе вы захватываете переменную цикла, которая считается вредной ( часть 1 ; часть 2 ).

Но да, вы, вероятно, должны использовать вместо этого Parallel.ForEach. Однако стоит понять, почему это не удалось.

1 голос
/ 04 марта 2011

Поскольку вы вызываете task.Result для каждой задачи, вы создаете блокирующий вызов, который заставляет цикл ждать, пока эта задача не вернется.

Сначала вы должны создать все задачи, а затем прочитать результаты в отдельном цикле.

1 голос
/ 04 марта 2011

Вызов 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);

    // /8237233/parallelnye-zadachi-obrabatyvaytsya-kak-posledovatelnye
    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());
...