Список задач не дает мне то, что я ожидаю - PullRequest
0 голосов
/ 02 мая 2018

Когда я запускаю следующее, я получаю значение 100 для Задачи № {0} и cb

Если я построчно отлаживаю в цикле, я получаю правильный ответ.

Как это исправить?

public static void TaskList()
{
    ConcurrentBag<int> cb = new ConcurrentBag<int>();
    List<Task> taskArray = new List<Task>();
    for (int i = 0; i < 100; i++)
    {
        taskArray.Add(Task.Factory.StartNew((Object obj) => {
            int j = 0 + i;
            cb.Add(j);
            Debug.WriteLine("Task #{0} created on {1}",
                                j, Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(10);
        },
                                                i));
    }
    Task.WaitAll(taskArray.ToArray());
    foreach(var v in cb)
    {
        Debug.WriteLine(v);
    }
    Debug.WriteLine("");
}

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Вы видите 100 распечатку почти для каждого запуска, потому что i в следующей строке эквивалентно 100 к моменту запуска задач:

int j = 0 + i;

Я полагаю, что вы пытаетесь решить эту проблему с помощью строки int j = 0 + i, но, поскольку i - это то, что меняется, j все равно всегда будет эквивалентно тому, что i будет при запуске задачи. , Если вы назначите значение i для j вне вашей задачи, эта проблема не появится:

int j = i;

taskArray.Add(Task.Factory.StartNew((Object obj) => {
    cb.Add(j);
    Console.WriteLine("Task #{0} created on {1}",
                        j, Thread.CurrentThread.ManagedThreadId);
    Thread.Sleep(10);
}, , i));

Не уверен, что я проделал большую работу, объясняя это, но я надеюсь, что это поможет.

0 голосов
/ 02 мая 2018

Вы передаете измененное замыкание (i) внутри своих задач.

Когда Task эффективно выполняется, значение i не определяется (в вашем случае это 100, потому что задание запускается после завершения цикла for).

Вы должны избегать захвата измененных замыканий внутри делегатов, которые выполняются лениво (например, ваш фрагмент Task, но то же самое может произойти с использованием LINQ на IEnumerable).

Вместо этого присвойте свое значение локальной переменной и передайте его внутри действия Task:

for (int i = 0; i < 100; i++)
{
    var count = i;
    taskArray.Add(Task.Factory.StartNew((Object obj) => {
        int j = 0 + count;
        cb.Add(j);
        Debug.WriteLine("Task #{0} created on {1}",
                            j, Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(10);
    },
                                            i));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...