Почему вывод основного потока на первом месте в 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(".");
        }
    }
}

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

есть ли объяснение этому?

7 56

7 ответов:

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

нить.Начать Метод

1) нить.Метод Start приводит к изменению состояния операционной системы текущий экземпляр в ThreadState.Бегущий.

2) Как только поток находится в состоянии потока.Идущее государство, работая система может запланировать его для выполнения. Поток начинает выполняться по адресу первая строка метода представлена ThreadStart

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

enter image description here

вы говорите:

" это странно для меня, потому что сначала начинается поток t, а затем продолжается основной.".

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

скорее совет, чем не ответ:

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


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


на практике это будет выглядеть так:

вы должны объявить два ManualResetEvents, которые видны как для основного, так и для фона нить такая:

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(".");
        }
    }

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

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

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

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

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

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