2015-06-01 6 views
56

Я написал эту небольшую программу:Почему основной поток выводится первым в C#?

class Program 
{ 
    static void Main(string[] args) 
    { 
     Thread t = new Thread(WriteX); 
     t.Start(); 

     for (int i = 0; i < 1000; i++) 
     { 
      Console.Write("O"); 
     } 
    } 

    private static void WriteX() 
    { 
     for (int i = 0; i < 1000; i++) 
     { 
      Console.Write("."); 
     } 
    } 
} 

Я побежал около пятидесяти раз, и первый символ на консоли всегда был «O». Это странно для меня, потому что нить t начинается сначала, а главное продолжается.

Есть ли какие-либо объяснения для этого?

+51

(не) удача. Нет никакой гарантии, что O будет первым, но накладные расходы при создании вторичного потока сделают очень маловероятным, что второй поток в конечном итоге достигнет финишной черты. Вы можете сравнить это с стоячей 20 см от финиша, а затем кричать «Господа, запустите свои двигатели и идите!» и затем немедленно сделайте шаг над финишной чертой. Шанс присутствует, но незначительный. –

+1

Поведение не является детерминированным и зависит от планировщика потоков ОС. Не забывайте, что создание самого потока имеет накладные расходы. Попробуйте запустить его достаточно времени, и вы увидите его в разных вариантах. –

+1

Это может относиться к времени, которое требуется для запуска потока (в то время mainthread продолжает свою собственную работу ... иначе он запускает цикл for). Пробовали ли вы, что происходит, когда вы ставите короткий сон после t.Start() в основном (например, около 30 миллисекунд)? – Thomas

ответ

53

Это, вероятно, потому, что Thread.start первый вызывает изменение состояния нити, на которой она называется и графики ОС на исполнение аосновной поток уже запущен и не нужны эти два шага. Вероятно, это причина, по которой оператор в основном потоке выполняет первую, а во вновь созданном потоке. Имейте в виду, что последовательность выполнения потоков не гарантируется.

Thread.Start Method

1) Метод Thread.start Вызывает операционную систему, чтобы изменить состояние тока экземпляра к ThreadState.Running.

2) Как только поток находится в состоянии ThreadState.Running, операционная система может запланировать ее выполнение. Нить начинает выполнение в первой строки методы, представленного ThreadStart

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

enter image description here

4

В основном требуется время для запуска потока вверх. Вы выполняете код потока одновременно с остальной частью первого метода. Поэтому, принимая во внимание время, необходимое для запуска потока, а затем дойти до точки, где он пишет «.». Имеет ли это смысл?

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

+1

Я не конечно, если ваша инструкция кэширования потоков является точной. Мое понимание было вручную создано нитями не пилить или добавить в пул потоков. Чтобы использовать поток пула потоков, вам нужно либо использовать TPL, либо текущий планировщик, либо более старый материал ThreadPool. –

+0

Спасибо за это, я обновил его сейчас (надеюсь, что-то правильно). Чтобы быть справедливым, я только начал экспериментировать с Multithreading и TPL, и я просто заметил это поведение и предположил, что он кэширует его. Я предполагаю, что имеет смысл, что он уже существует, поэтому ему не нужны накладные расходы, чтобы начать его снова. – Keithin8a

4

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

+6

Это не отвечает на вопрос. – edc65

+2

Обвинитель уже подумал, что это так, поэтому вопрос. – Dukeling

+1

@ Dukeling Аскер ожидал недетерминированного результата и смущен, потому что (ее /) его результат _seemingly_ детерминирован, поэтому я думаю ** edc65 ** прав. – mg30rg

4

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

13

Больше совета, чем не ответ:

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


Если вы хотите, чтобы ваши темы на «раса» для контроля, не г ive ваша основная нить на голову!Создание потока имеет некоторые накладные расходы, и основной поток уже создан (поскольку он создает другой поток). Если вы ищете в основном равные шансы как для основного, так и для рабочего потока, вам следует подождать, пока ваш рабочий поток будет создан в основном потоке и дождитесь, когда основной поток начнет гонку в фоновом потоке. Это может быть достигнуто synch objects.


На практике это будет выглядеть следующим образом:

Вы должны объявить два ManualResetEvent S, которые видны как для вашего Main- и фоновый поток, как это:

private static ManualResetEvent backgroundThreadReady = new ManualResetEvent(false); 
private static ManualResetEvent startThreadRace = new ManualResetEvent(false); 

Затем в основном потоке вы должны дождаться инициализации своей нити, например:

static void Main(string[] args) 
{ 
    Thread t = new Thread(WriteX); 
    t.Start(); 
    backgroundThreadReady.WaitOne(); // wait for background thread to be ready 

    startThreadRace.Set();   // signal your background thread to start the race 
    for (int i = 0; i < 1000; i++) 
    { 
     Console.Write("O"); 
    } 
} 

И в вашей теме:

private static void WriteX() 
    { 
     backgroundThreadReady.Set(); // inform your main thread that this thread is ready for the race 

     startThreadRace.WaitOne(); // wait 'till the main thread starts the race 
     for (int i = 0; i < 1000; i++) 
     { 
      Console.Write("."); 
     } 
    } 

Обратите внимание, что я мог бы использовать другие объекты waitable синхронизации (семафор, автовозврата событие, даже критический замок раздел с некоторыми хака, я только выберите самое простое, быстрое решение, которое можно легко расширить).

20

Вы говорите:

«Это странно для меня, потому что т нить начинается первый, то основной продолжается.».

Это неправда. «Основной» протектор уже запущен. Когда выполняется t.Start();, OS сообщается, что t находится в рабочем состоянии. Затем ОС заплатит время выполнения для потока «скоро». Это что-то другое, кроме того, что ОС инструктируется прекратить выполнение этого потока до тех пор, пока не начнется поток t. Другими словами, когда возвращается Start, нет гарантии, что поток уже начал выполнение.

+1

Короткие и на месте. Поток * разрешен * для запуска, а 'Start' не * wait * для его фактического начала выполнения. – Luaan

0

Существует только одна причина, по которой основной поток будет завершен до созданного потока, и это потому, что для начала потока требуется время. Единственный раз, когда вы будете использовать потоки для ускорения работы программы, - это то, когда 2 задачи могут выполняться в одно и то же время. Если вы хотите сделать второй цикл первым, взгляните на Parallel.For петли в C# ... они будут запускать каждый цикл в цикле for в одно и то же время (не все из них, но столько, сколько ваш компьютер может обрабатывать)

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