2009-08-28 2 views
1

Я создал простое консольное приложение .NET, и в нем я запускаю 3 управляемых потока. Каждый поток выполняет следующий цикл:Приоритет нитей не влияет?

 while (true) 
     { 
      System.Console.WriteLine(thread.Name + " " + DateTime.Now); 
      Thread.Sleep(10); 
     } 

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

Что касается приоритетов потоков, я не могу здесь объяснить это?

ответ

7

Попробуйте Sleep(1) вместо этого. 10 мс - это вечность в поточном времени, однако Sleep(1) в основном говорит: «Я отдаю оставшуюся часть своего времени на выполнение для другого потока», который, я думаю, является тем, что вы хотите. Приоритет потока не просто делает поток выполняться чаще, он просто говорит: «Если у планировщика есть выбор между предоставлением потока A временным листом и потоком B времени, дайте ему тот, который имеет более высокий приоритет». Попробуйте это изменение и посмотрите, что произойдет. В качестве альтернативы заставляйте каждую нить занимать различное количество времени для выполнения (не используйте для этого спящий режим, напечатайте 100 элементов в одном контуре, напечатайте 1000 на другом и т. Д.)

Редактирование: изменено на «Сон» (см. 1), не уверен, почему он был изменен. Смотрите мой комментарий ниже, почему Sleep (1) - лучший выбор здесь.

+1

Чтобы "дать остальную часть моего кванта времени для выполнения другого потока" Я хотел бы предложить Sleep (0) вместо сна (1). – ChrisW

+1

Это распространенное заблуждение, но Sleep (1) - это то, что вы хотите. Sleep (0) дает исполнение только для потоков с одинаковым приоритетом. Sleep (1) приведет к любому потоку, доступному для исполнения. Вот хороший блог на эту тему: http://www.bluebytesoftware.com/blog/PermaLink,guid,1c013d42-c983-4102-9233-ca54b8f3d1a1.aspx Я думал, что Sleep (0) был верным в течение многих лет, но я был неправильно. –

+0

Спасибо. (от 15 до 15 символов) – ChrisW

0

Нити не будут писать чаще на консоль, потому что все они ждут 10 мс между записью. Тем не менее, поток с высоким приоритетом должен записывать на консоль более регулярно, т. Е. Ближе к истинному 10 мс между режимами записи. Это связано с тем, что, хотя потоки с более низким приоритетом могут быть отброшены другими задачами на ПК, потоку с более высоким приоритетом придется ждать меньше, чем разрешено писать.

Ответит ли это на ваш вопрос?

1

Если вы потеряли операторы Thread.Sleep, вы, скорее всего, увидите больше действий из потока с более высоким приоритетом. Thread.Sleep дает другим потокам много времени, чтобы сделать свое дело.

+0

Интересно отметить, что когда я вынул вызов, чтобы спать вообще, Windows справилась с плохой работой по управлению приоритетом. Грубый% процессорного времени для потоков был 28, 43 и 27, причем поток с высоким приоритетом получал наименьшее время. Странный. – 2009-08-29 18:04:25

+0

Более случайное поведение произошло, когда я начал 10 потоков нормального приоритета и без вызовов Sleep(). Было явление четной/нечетной нити, где все четные потоки получили удвоенное время появления нечетных потоков. Запустите его снова, и теперь нечетные потоки удвоились. Казалось, что он снова и снова переключается. Включение вызовов Sleep() позволяет всем потокам получать равное время. Кажется, что призывы к Sleep() необходимы для «согласования» потоков друг с другом, несправедливое бремя для Windows для размещения приложений. – 2009-08-29 18:09:13

3

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

+0

+1 - он получит лучшую картину, выполняя то, что на самом деле требует нетривиальных циклов. – DarkSquid

+0

Спасибо. Thread.Sleep (10) действительно был ключом к тому, что он позволяет другим потокам работать. Сокращение времени до 1 мс дало мне результаты, которые я ожидал. Кроме того, когда потоку с высоким приоритетом давалась больше работы, он, похоже, получал еще больше времени процессора. – 2009-08-29 18:03:38

1

ThreadPriority не может вести себя, как ожидалось ... некоторые материалы для чтения на приоритет потока ...

Приоритеты темы злы
http://www.codinghorror.com/blog/archives/000671.html

ThreadPriority.BelowNormal
http://odetocode.com/Blogs/scott/archive/2006/08/27/6053.aspx

Почему Сон (1) лучше, чем сна (0)
http://www.bluebytesoftware.com/blog/PermaLink,guid,1c013d42-c983-4102-9233-ca54b8f3d1a1.aspx

1

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

При выполнении потока с высоким приоритетом планировщик может по-прежнему планировать поток с низким приоритетом на другие ядра, поэтому вы не видите существенной разницы.

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

Я не знаю, как вы можете сделать это в .NET, но в C следует использовать SetThreadAffinityMask функцию:

HANDLE hThreadLow = CreateThread(NULL, 0, ThreadProc1, 
    NULL, CREATE_SUSPENDED, &dwThreadLowId); 

HANDLE hThreadHigh = CreateThread(NULL, 0, ThreadProc2, 
    NULL, CREATE_SUSPENDED, &dwThreadHighId); 

SetThreadPriority(hThreadLow, THREAD_PRIORITY_BELOW_NORMAL); 
SetThreadPriority(hThreadHigh, THREAD_PRIORITY_ABOVE_NORMAL); 

SetThreadAffinityMask(hThreadHigh, 0x00000001); 
SetThreadAffinityMask(hThreadLow, 0x00000001); 

ResumeThread(hThreadLow); 
ResumeThread(hThreadHigh); 

Теперь вы заметите существенную разницу между этими двумя потоками. Внедрите счетчик в каждом из двух потоков и увидите разницу в диапазоне от 100x10000x в подсчитанных значениях.

1

Вдохновленный из @Karim Agha «s answer here

Я считаю, что это из-за мульти-процессорных ядер

с простой программой, чтобы доказать это, установите оба потока работают на 1-ом ядре CPU:

process.ProcessorAffinity = (IntPtr) 0x0001; 

// 0x0001 = 0000 0001 - run on 1st core 
// 0x0002 = 0000 0010 - run on 2nd core 
// 0x0003 = 0000 0011 - run on 1st and 2nd core 
// 0x0004 = 0000 0100 - run on 3rd core 
// and so on 

И вот, я получу что-то вроде этого highCount:5179 & lowCount:167. Если я прокомментирую эту строку, это будет примерно так: highCount:3350 & lowCount:3382.

Дальнейшие доказательства будут получены от Task Manager, откройте его и сыграйте с настройками Set affinity, и вы увидите немедленные эффекты, применяемые к обоим ниткам.

enter image description here

enter image description here

using System; 
using System.Diagnostics; 
using System.Threading; 

namespace Test 
{ 
    class Program 
    { 
     private static int highCount, lowCount; 

     private static readonly ManualResetEventSlim Signal = new ManualResetEventSlim(false); 

     public static void Main() 
     { 
      using (var process = Process.GetCurrentProcess()) 
      { 
       process.PriorityClass = ProcessPriorityClass.High; 

       // only run on core number 1 
       process.ProcessorAffinity = (IntPtr) 0x0001; 
      } 

      var slowThread = new Thread(() => 
      { 
       while (true) 
       { 
        if (Signal.IsSet) 
        { 
         break; 
        } 
        Console.WriteLine("Lowest"); 
        lowCount++; 
       } 
      }) 
      { 
       Name = "Lowest", 
       Priority = ThreadPriority.Lowest 
      }; 

      var fastThread = new Thread(() => 
      { 
       while (true) 
       { 
        if (Signal.IsSet) 
        { 
         break; 
        } 
        Console.WriteLine("Highest"); 
        highCount++; 
       } 
      }) 
      { 
       Name = "Highest", 
       Priority = ThreadPriority.Highest 
      }; 

      fastThread.Start(); 
      slowThread.Start(); 

      Console.ReadKey(); 

      Signal.Set(); 

      fastThread.Join(); 
      slowThread.Join(); 

      Console.WriteLine($"highCount:{highCount} & lowCount:{lowCount}"); 

      Console.ReadKey(); 
     } 
    } 
}