Экспорт типов документов с использованием очередей и многопоточности

Некоторое время мне было интересно увидеть, работают ли некоторые задачи, когда они разбиты на несколько потоков.

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

То, что я написал, похоже, работает, хотя я не уверен, что это лучший способ, или рекомендуемый способ сделать что-то подобное, поэтому я хочу спросить, что не так с моим кодом и как это можно сделать лучше. То, что он делает, не очень важно, и мне неинтересно, если эта задача должна быть разделена, но больше по тому, насколько хороший или плохой код, который я написал.

private static void ExportDocTypes(IEnumerable<string> docTypes)
{
    var queue = new Queue<string>(docTypes);
    var tasks = new List<Task>();
    for (byte i = 0; i < threadCount; i++)  //threadCount is a const set to 4.
    {
        Action a = () =>
        {
            while(queue.Count() > 0) {
                var type = queue.Dequeue();
                ExportDocuments(type, i);  //i is only sent for response.write
            }
        };
        var t = new Task(a);
        t.Start();
        tasks.Add(t);
        System.Threading.Thread.Sleep(10);
    }
    while (tasks.Count() > 0)
    {
        foreach (var t in tasks)
        {
            if (t.IsCompleted)
            {
                t.Dispose();
                tasks.Remove(t);
            }
        }
        System.Threading.Thread.Sleep(1000);
    }
}
39 голосов | спросил JonathanPeel 26 J0000006Europe/Moscow 2014, 10:40:47

3 ответа


89

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

  

Каковы наилучшие методы многопоточности в C #?

  1. Не надо. Многопоточность - плохая идея. Программы достаточно сложны, чтобы понять уже с помощью single точки управления потоком в них; почему бы вам захотеть добавить второй, третий, четвертый ...? Вам нравится отслеживать невероятно трудные в воспроизведении, невозможные для понимания ошибки? У вас есть энциклопедическое знание различий между слабыми и сильными моделями памяти? Нет? Тогда не пишите многопоточные программы! Процессы - совершенно хорошая единица работы; если у вас есть работа, создайте процесс.

  2. О, так что вы хотите делать многопоточное программирование в любом случае. В этом случае используйте самый высокий уровень абстракции, доступный . Прямое программирование против потоков означает, что вы пишете программы о workers , когда вы можете писать программы о jobs . Помните, что рабочие - это просто средство для достижения цели; то, что вы действительно хотите сделать, это задания. Используйте параллельную библиотеку задач . : задачи jobs , потоки - это работники . Пусть TPL выяснит, как эффективно использовать работников.

  3. Подождите, вернемся к пункту 1 снова. Спросите себя действительно ли вам нужен параллелизм? С истинным параллелизмом у нас есть две или более точки управления в программе , фактически активные одновременно . С симулированным параллелизмом две точки управления чередуются с использованием процессора, поэтому они фактически не параллельны. В большинстве случаев мы заканчиваем симулированным параллелизмом, потому что есть больше потоков, чем есть процессоры. Это естественным образом приводит нас к выводу, что, возможно, фактический параллелизм - это не то, что нам нужно. Большую часть времени, что действительно хотят люди, это asynchrony ; параллелизм - один из способов достижения асинхронности, но это не единственный способ. Использовать новую функцию async /wait в C # 5 , чтобы представлять асинхронные рабочие процессы.

  4. Если вы смущены предыдущей точкой и думаете подождать минуту, нет способа добиться асинхронности без нескольких потоков управления в процессе, прочитайте статью Стивена Клири Нет темы . Не пытайтесь писать многопоточные программы, пока не поймете это.

  5. Во всех случаях избегать разделяемой памяти . Большинство ошибок с чередованием вызваны непониманием семантики разделяемой памяти в реальном мире. Если вы должны создавать потоки, относитесь к ним так, как если бы они были процессами: дайте им все, что им нужно, чтобы выполнить свою работу и позволить им работать без , изменяя память, связанную с любым другим потоком. Точно так же, как процесс не может изменить память любого другого процесса.

  6. О, так что вы хотите делиться памятью по потокам, хотя это невероятно уязвимо? Опять же, позвольте мне вернуться к использовать инструмент высочайшего уровня, доступный вам . Эти инструменты были написаны экспертами. Ты не эксперт. Знаете ли вы, как сделать лениво-вычисленное значение, записанное в поле, так что вы абсолютно гарантированы , что ленивое вычисление выполняется не более одного раза, и поле никогда не наблюдается в несогласованном состоянии no независимо от того, что находится в любом кэше процессора? Я не. Вероятно, вы этого не сделаете. Joe Duffy, поэтому вы должны использовать Lazy<T> , он написал, а не пытался написать свой собственный.

  7. О, так вы хотите написать свой собственный код, который разделяет память? Затем возьмите блокировку ради бога. Я вижу так много ошибок кода, где люди получают в свои головы, что блокировки стоят дорого, и они пишут этот запутанный божественный неправильный код, который уклоняется от блокировки, которая стоила бы им двенадцати наносекунд , чтобы просто взять. Избегать блокировки, чтобы сэкономить на ее стоимости, - это жалоба на то, что электричество занимает слишком много времени, чтобы добраться до лампочки, когда вы переворачиваете переключатель. Замки - это мощный инструмент для обеспечения согласованности памяти; если вы собираетесь программировать общую память на уровне нити, вы избегаете блокировок на свой страх и риск.

  8. Если вы используете Thread.Sleep с аргументом, отличным от нуля или одним в любом производственном коде, вы, возможно, что-то не так. Нитки дороги; вы не платите работнику, чтобы спать, поэтому не платите нить, чтобы спать. Если вы используете сон для решения проблемы правильности, избегая проблемы с синхронизацией - как вы, кажется, в вашем коде, - тогда вы определенно сделали что-то глубоко неправильное. Многопоточный коддолжно быть правильным независимо от аварийного времени .

ответил Eric Lippert 26 J0000006Europe/Moscow 2014, 19:17:29
17

Вы можете использовать класс Parallel:

private static void ExportDocTypes(IEnumerable<string> docTypes)
{
    int i = -1;
    Parallel.ForEach(docTypes, docType => ExportDocuments(docType, Interlocked.Increment(ref i)));
}

http: //msdn.microsoft.com/en-us/library/system.threading.tasks.parallel_methods%28v=vs.110%29.aspx

ответил Nikita B 26 J0000006Europe/Moscow 2014, 13:43:11
11

Я бы сделал что-то вроде этого:

private static void ExportDocTypes(IEnumerable<string> docTypes)
{
    var queue = new ConcurrentQueue<string>(docTypes);
    var tasks = new List<Task>();

    for (byte i = 0; i < threadCount; i++)  //threadCount is a const set to 4.
    {
        byte iteration = i;
        Task t = Task.Factory.StartNew(() =>
        {
            string type;
            bool queueNotEmpty = queue.TryDequeue(out type);
            while(queueNotEmpty) 
            {
                ExportDocuments(type, iteration);  //i is only sent for response.write
                bool queueEmpty = queue.TryDequeue(out type);
            }
        });
        tasks.Add(t);
    }

    Task.WaitAll(tasks.ToArray());
    tasks.ForEach(task => task.Dispose());
}

Использование таких вещей, как ConcurrentQueue, так как многие потоки будут одновременно обращаться к нему. Также более эффективно использовать Linq и статические методы в Task для проверки завершения, а не для использования Thread.Sleep.

Edit: Кроме того, это пропустило ранее, вам нужно сделать локальную копию i в цикле for, иначе i будет указывать на текущее значение, вместо этого от захваченного значения на этой итерации. Что-то, о чем нужно знать при нерестах Задачи в циклах.

ответил patchandthat 26 J0000006Europe/Moscow 2014, 10:51:07

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132