C # Task.Run пропускает значения, пропускает данные, когда делает много итераций - PullRequest
1 голос
/ 18 мая 2019

При выполнении тысяч итераций одной и той же функции при использовании Task.Run конечный результат при отслеживании того, сколько раз значение было увеличено, приводит к тому, что общее количество итераций не совпадает с тем, сколько раз значение было увеличено,Зачем?Как исправить?Предлагаемые альтернативные способы?Моя цель состоит в том, чтобы просто иметь возможность выполнять ту же функцию в многопоточной среде как можно быстрее.

Пример кода ниже показывает, что я пытаюсь сделать сейчас, вы должны иметь возможность запустить его как есть, чтобы увидеть похожие результатыкак я вижу:

taskNumber = 9980, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 9998, nonTaskIterations = 10000
taskNumber = 9998, nonTaskIterations = 10000
taskNumber = 9999, nonTaskIterations = 10000
taskNumber = 9997, nonTaskIterations = 10000
taskNumber = 9999, nonTaskIterations = 10000
taskNumber = 9997, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 9995, nonTaskIterations = 10000
taskNumber = 9994, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 9998, nonTaskIterations = 10000
taskNumber = 9999, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 9999, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
    static void Main(string[] args)
    {
        for (int i = 0; i < 20; i++)
        {
            //iterate many times to see different outputs
            PrimaryFunction();
        }

        Console.WriteLine("why the hell does TaskNumber not always == nonTaskIterations value?");
        Console.ReadLine();
    }

    static void PrimaryFunction()
    {
        long taskNumber = 0;
        int nonTaskIterations = 0;

        for (int i = 0; i < 100; i++)
        {
            List<Task> tasks = new List<Task>();
            for (int j = 0; j < 100; j++)
            {
                nonTaskIterations++;
                tasks.Add(Task.Run(() =>
                {
                    taskNumber = taskNumber + 1;
                    DoWork(taskNumber);
                }));
            }

            Task.WaitAll(tasks.ToArray());
        }

        Console.WriteLine("taskNumber = " + taskNumber + ", nonTaskIterations = " + nonTaskIterations);
    }

    static void DoWork(long i)
    {
        //do work here
        Random random = new Random();
        var x = random.Next(0, 10).ToString();
    }

Я ожидаю увидеть это каждый раз, когда он запускается:

taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000

Я открыт для предложений о том, как исправить это или альтернативныйбыстрые и эффективные, но в то же время точные способы многократного запуска функции многопоточной среды без пропуска приращенных значений.

1 Ответ

2 голосов
/ 18 мая 2019

Это просто не потокобезопасно (и я уверен, что вы сами пришли к такому выводу).Нельзя ожидать, что несколько потоков на нескольких ядрах всегда будут иметь одинаковое значение в кэше

Один из способов получить ожидаемые значения -используйте Interlocked.Increment , который действует как барьер памяти , устраняя способность процессора переупорядочивать обращения к памяти вокруг инструкции и, в свою очередь, гарантирует, что операции приращения будут атомарными

Увеличивает указанную переменную и сохраняет результат как элементарную операцию.

Interlocked.Increment(ref someValue);

Вывод

taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000

Полная демонстрация здесь

...