#c# #.net #multithreading #.net-core
#c# #.net #многопоточность #.net-core
Вопрос:
Итак, я изучаю класс .NET Thread на основе книги: Экзамен Ref 70-483: Программирование на C #, и я заметил кое-что, чего я не знаю, почему это происходит или должно ли это происходить.
Книга дает мне эту часть кода на странице 4:
public static class Program
{
public static void ThreadMethod()
{
for (int i = 0; i < 10; i )
{
Console.WriteLine("ThreadProc: {0}", i);
Thread.Sleep(0);
}
}
public static void Main()
{
Thread t = new Thread(new ThreadStart(ThreadMethod));
t.Start();
for (int i = 0; i < 4; i )
{
Console.WriteLine("Main thread: Do some work.");
Thread.Sleep(0);
}
t.Join();
}
}
С ожидаемым результатом, показанным ниже, поскольку в каждом потоке.В режиме ожидания (0) текущий поток завершает работу и переходит к следующему потоку в очереди:
// Main thread: Do some work.
// ThreadProc: 0
// Main thread: Do some work.
// ThreadProc: 1
// Main thread: Do some work.
// ThreadProc: 2
// Main thread: Do some work.
// ThreadProc: 3
// ThreadProc: 4
// ThreadProc: 5
// ThreadProc: 6
// ThreadProc: 7
// ThreadProc: 8
// ThreadProc: 9
Но когда я создал свою версию этого, она работала не так, как ожидалось, даже когда я скопировал вставленный код и запустил на своей машине, он давал этот результат каждый раз, когда a выполнял программу с тем же кодом, что и выше:
// Main thread: Do some work.
// ThreadProc: 0
// ThreadProc: 1
// ThreadProc: 2
// ThreadProc: 3
// ThreadProc: 4
// ThreadProc: 5
// ThreadProc: 6
// ThreadProc: 7
// ThreadProc: 8
// ThreadProc: 9
// Main thread: Do some work.
// Main thread: Do some work.
// Main thread: Do some work.
Как вы можете видеть, кажется, что он входит в боковой поток и когда я вызываю поток.Метод Sleep (0) не возвращается к основному потоку, как следовало бы, и продолжает работать с боковым потоком, пока он не завершится.
В качестве обходного пути для этого я использовал класс Random для генерации случайного числа от 100 до 500 для передачи в поток.Метод Sleep (), и он начинает работать, как ожидалось:
public static class Program
{
public static void ThreadMethod()
{
var rand = new Random();
for (int i = 0; i < 10; i )
{
Console.WriteLine("ThreadProc: {0}", i);
Thread.Sleep(rand.Next(100, 500));
}
}
public static void Main()
{
Thread t = new Thread(new ThreadStart(ThreadMethod));
t.Start();
var rand = new Random();
for (int i = 0; i < 4; i )
{
Console.WriteLine("Main thread: Do some work.");
Thread.Sleep(rand.Next(100, 500));
}
t.Join();
}
}
Результаты выглядят следующим образом:
// Main thread: Do some work.
// ThreadProc: 0
// ThreadProc: 1
// ThreadProc: 2
// Main thread: Do some work.
// ThreadProc: 3
// Main thread: Do some work.
// ThreadProc: 4
// Main thread: Do some work.
// ThreadProc: 5
// ThreadProc: 6
// ThreadProc: 7
// ThreadProc: 8
// ThreadProc: 9
Я не думаю, что книга была написана на платформе .NET Core, и я подумал, что эта «проблема» может быть вызвана изменениями в контексте синхронизации между этими двумя фреймворками.
Я проверил приоритет двух потоков и установил значение по умолчанию «Normal».
У кого-нибудь есть представление о том, почему это происходит?
Комментарии:
1.
and when i call Thread.Sleep(0) method it don't go back to the main thread as it should
Где в документах говорится, что это должно быть?2. @mjwills Ну, в документации указано, что
Thread.Sleep(0)
это приводит к другому потоку, который готов к запуску, поэтому это может сбить с толку. Хитрость в том, что в этом примере нет потока в состоянии готовности к запуску, поскольку оба потока запланированы на своем собственном ядре и не должны конкурировать за ресурсы3. Так это то, что
If there are no other threads of equal priority that are ready to run, execution of the current thread is not suspended.
означает @KevinGosse?4. @mjwills Да.
Thread.Sleep(0)
означает «проверьте, ожидает ли другой поток запуска. Если это так, пусть он запускается вместо этого. Если нет, продолжайте работать «. Это было очень полезно, когда процессоры имели одно ядро, в настоящее время оно используется только в примитивах спин-блокировки, я полагаю5. Потрясающе — вот почему я задал вопрос о OP. Поскольку в документах фактически не указано, что он / она сказал, что они заявили.
Ответ №1:
В этом примере вы не увидите никакой разницы в поведении между .net framework и .net core.
Книга была написана в эпоху, когда большинство потребительских процессоров были одноядерными. С одноядерным процессором одновременно может быть запланирован только один поток, и поэтому результат выборки предсказуем.
В настоящее время практически все процессоры имеют несколько ядер, что означает, что несколько потоков могут быть запланированы одновременно. Это делает вывод программы непредсказуемым и не зависит от вызовов Thread.Sleep(0)
.
Один из способов добиться поведения, описанного в книге о современном оборудовании, — установить привязку вашего процесса так, чтобы он выполнялся на одном ядре:
> start /affinity 1 CoreConsoleApp1.exe
Делая это, я получаю ожидаемый чередующийся вывод:
ThreadProc: 0
Main thread: Do some work.
ThreadProc: 1
Main thread: Do some work.
ThreadProc: 2
Main thread: Do some work.
ThreadProc: 3
Main thread: Do some work.
ThreadProc: 4
ThreadProc: 5
ThreadProc: 6
ThreadProc: 7
ThreadProc: 8
ThreadProc: 9
Ответ №2:
Не ожидайте определенного поведения с потоками. Если вы собираетесь синхронизировать, не используйте функцию sleep.
Попробуйте изменить основной цикл на 10000 циклов и проделайте то же самое с циклом потока.
Вы увидите гораздо лучший и ожидаемый результат. Тот факт, что он может предоставить оставшийся временной интервал другому потоку, не означает, что это не будет новый поток, который вы создаете (вместо основного)
То есть .net framework определяет оптимальный способ планирования времени потока, и никаких гарантий нет. Thread.Sleep
Функция предназначена для того, чтобы просто убедиться, что другие потоки когда-нибудь в будущем будут реагировать.