3

Следующий код 5 потоков с разными приоритетами конкурируют за доступ к процессору с 8 ядрами (Mac OS X 10.8.5, Mono). Каждый поток увеличивает счетчик.Врезка с наименьшим приоритетом вызывается больше раз

using System; 
using System.Threading; 

     class PriorityTesting 
    { 
     static long[] counts; 
     static bool finish; 

     static void ThreadFunc(object iThread) 
     { 
     while(true) 
     { 
      if(finish) 
       break; 
      counts[(int)iThread]++; 
     } 
     } 

     static void Main() 
     { 
     counts = new long[5]; 
     Thread[] t = new Thread[5]; 
     for(int i=0; i<t.Length; i++) 
     { 
      t[i] = new Thread(ThreadFunc); 
      t[i].Priority = (ThreadPriority)i; 
     } 
     // Запускаем потоки 
     for(int i=0; i<t.Length; i++) 
      t[i].Start(i); 

     // Даём потокам возможность поработать 10 c 
     Thread.Sleep(10000); 

     // Сигнал о завершении 
     finish = true; 

     // Ожидаем завершения всех потоков 
     for(int i=0; i<t.Length; i++) 
      t[i].Join(); 
     // Вывод результатов 
     for(int i=0; i<t.Length; i++) 
      Console.WriteLine("Thread with priority {0, 15}, Counts: {1}", (ThreadPriority)i, counts[i]); 
     } 
    } 

Компиляция:

$ mcs PriorityTesting.cs 
$ mono PriorityTesting.exe 

Выходные:

Thread with priority   Lowest, Counts: 178544880 
Thread with priority  BelowNormal, Counts: 167783608 
Thread with priority   Normal, Counts: 160593225 
Thread with priority  AboveNormal, Counts: 79123315 
Thread with priority   Highest, Counts: 81623159 

Как это что нить с самым низким приоритетом вызывается больше раз, чем нитей с наивысшим приоритетом?

UPD:

Тот же самый код на процессоре с 2 ядрами дает (Windows, .NET):

Thread with priority   Lowest, Counts: 7608195 
Thread with priority BelowNormal, Counts: 10457706 
Thread with priority   Normal, Counts: 17852629 
Thread with priority AboveNormal, Counts: 297729812 
Thread with priority  Highest, Counts: 302506232 

Почему разница?

+1

Вы заметили другое поведение, если вы исправили код, чтобы переменная 'finish' была отмечена' volatile' (как и должно быть)? Конечно, я хватаюсь за соломинку, но это _might_ имеет значение, в зависимости от того, на каком оборудовании вы работаете. –

+1

Я бы как-то изменил «ThreadFunc», чтобы дать больше шансов переключить потоки, такие как Thread.Yield или Console.WriteLine ... Может быть, из-за столь быстрого цикла результаты искажены. –

+1

Какая операционная система вы протестировали? –

ответ

6

Поддержка Priority не реализованный в Mono, поэтому поведение, которое вы видите о самом низком, вызываемом больше раз, может быть просто удачей.

Существует запрос на получение от вкладчика, реализующего это свойство, в github, поэтому вы можете присоединиться к нему, попросив его ознакомиться с ним.

ОБНОВЛЕНИЕ: Этот ответ может быть устаревшим. Поскольку Mono быстро продвигается вперед, лучше перепроверить свою программу, возможно, в настоящее время работает Priority.

+1

Это кажется правильным. Просто протестировав код и на машине win7 со средой выполнения MS.Net, он выдает ожидаемые результаты (низкое количество приоритетов при наименьшем числе, высокий приоритет). –

+1

@MarvinSmit вы запускали в выпуске или отладке? Я все еще получаю очень высокое значение для самого низкого приоритета при компиляции для выпуска. Другие четыре близки, но все еще не идеальны. Процессорные возможности также будут фактором (количество ядер/логических потоков). – Moho

+2

Обновление: после дополнительного тестирования код OP также работает некорректно с использованием .Net на Win7 (версия MS) –

1

Сначала вы запускаете нисходящий поток с наименьшим приоритетом. Это приводит к тому, что время нисходящих потоков с более низким приоритетом запускается при запуске потоков с более высоким приоритетом (запуск потока не является тривиальной операцией).

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

static long[] counts; 
    static bool finish; 
    static bool count; // add this 

    static void ThreadFunc(object iThread) 
    { 
    while(true) 
    { 
     if(finish) 
      break; 

     if(count) // only count when ready 
      counts[(int)iThread]++; 
    } 
    } 

В Main:

// After your loop to start the threads 
    // set the count flag to start counting 
    count = true; 
    // Даём потокам возможность поработать 10 c 
    Thread.Sleep(10000); 
+1

Отсутствует волатильность для флага, что может привести к очень неожиданным результатам. – Voo

1

Прежде всего - не возитесь с приоритетом потока. Или приоритет процесса. Вы ничего не будете помогать, и вы, вероятно, столкнетесь с множеством тупиков и других проблем синхронизации, которые вам понадобятся, и для отладки будет много fun. Доверяйте планировщику потоков ОС, чтобы выполнить свою работу - на самом деле это очень хорошо!

Во-вторых, Mono не поддерживает приоритеты.

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

Не просто «глупый» счетчик - добавьте глупую работу для процессора на каждом шаге счетчика, например Thread.SpinWait(100000).Это наложит немного глупости на процессор, что сделает ваш счет более интересным и сопоставимым.

Я никогда не видел хорошего случая для изменения приоритетов потоков. Всегда есть лучшее решение, и вы, скорее всего, пытаетесь решить проблему, которая либо не существует, либо может быть решена намного лучше. Хорошая стартовая ссылка для проблем с приоритетами отключена от Джеффа Атвуда http://blog.codinghorror.com/thread-priorities-are-evil/.

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

Осторожно:

2

Это может произойти по многим причинам.

Есть что-то под названием «Приоритетное повышение», которое является одним из них, операционная система повысит приоритет (увеличит приоритет потоков временно) по разным причинам. Пример: поток только что получил сигнал события. UI, который передает сообщения и появляется новое сообщение, полученное в очереди, другая причина может быть голод.

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

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

В Windows есть служба, называемая менеджером баланса. Он запускает асинхронно на системную нить, ищущую голодные потоки; эти - это потоки, которые ожидали запустить в состоянии готовности для 4 секунд или дольше. Если он найдет один, он даст потоку временное повышение приоритета . Он всегда повышает приоритет голодной нити до уровня 1 5, независимо от его текущего значения. Это делается для борьбы с голодом, например, когда много потоков с более высоким приоритетом: постоянно работает, так что нити с более низким приоритетом никогда не получат шанс выполнить .

Цитата из книги Concurrent Programming on Windows-Joe Duffy

Update:

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

Для результатов с 8 ядрами: как указано, моно не поддерживает приоритет, сначала вы запустили нисходящий поток с низким приоритетом, и у вас достаточно процессоров для запуска запущенных потоков. Таким образом, здесь нет необходимости вытеснять темы с более низким приоритетом. Таким образом, операционная система позволяет запускать потоки без нарушения (вытеснения).Попробуйте протестировать с большим количеством потоков, чем у процессора. Именно здесь приоритет действительно вступает в картину.

+1

@downvoter Я ценю нисходящее движение, если вы можете дать повод? –

Смежные вопросы