Многопоточный вывод Console.WriteLine пуст с .NET, x64 - PullRequest
0 голосов
/ 19 декабря 2011

На MSDN имеется образец для .NET ThreadPool. Если я запускаю этот код, вывод становится совершенно ошибочным, иногда я получаю совершенно пустой вывод на консоли.

Если я добавлю вызов Thread.Sleep(), даже на несколько мс, вывод будет в порядке.

AFAIK Console.WriteLine() является поточно-ориентированным, поэтому выход всегда должен быть там. Но это не так, по крайней мере, в моей i7 2600 x64 скомпилированной версии. Очевидно, что если я добавлю точку останова, все будет хорошо, но это сводит меня с ума.

Я добавил ConcurrentBag, чтобы убедиться, что материал есть, но даже печать его элементов пуста. Опять же, если я добавлю точку останова, все будет хорошо.

{
    public class TaskInfo
    {
        public string m_text;

            public int m_value;
        public ConcurrentBag<int> m_bag;
    public TaskInfo(string text, int value, ConcurrentBag<int> bag)
    {
        m_text = text;
        m_value = value;
        m_bag = bag;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run();
    }

    void Run()
    {
        ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();
        for (int i = 0; i < 10; i++)
        {
            TaskInfo ti = new TaskInfo("Hello Thread", i, concurrentBag);
            bool b = ThreadPool.QueueUserWorkItem(new WaitCallback(MyThreadFunction), ti);
            if (!b)
            {
                Console.WriteLine("Damn!");
            }
            //Thread.Sleep(5);
        }

        for (int j = 0; j < concurrentBag.Count; j++)
        {
            Console.WriteLine("This is in the bag: {0}", concurrentBag.ElementAt(j));
        }
    }

    static void MyThreadFunction(object stateInfo)
    {
        TaskInfo ti = (TaskInfo)stateInfo;
        ti.m_bag.Add(ti.m_value);
        Console.WriteLine(ti.m_text + ti.m_value.ToString());
    }
}

}

Ответы [ 3 ]

1 голос
/ 19 декабря 2011

Это явно консольное приложение - после того, как вы поставили в очередь все рабочие элементы, метод Run возвращается, и ваша программа немедленно завершается.Он не будет ждать окончания ваших рабочих элементов.По крайней мере, добавьте Thread.Sleep после запуска, чтобы пул потоков завершился.

Правильнее всего использовать массив из ManualResetEvent экземпляров и ждать, пока все они будут Set каждым рабочим потоком.Поскольку это консольное приложение, вы не сможете использовать WaitHandle.WaitAll, если не украсите свой метод Main с помощью [STAThread].

1 голос
/ 19 декабря 2011

Я очень хорошо понимаю, что вывод может быть пустым.

Вы помещаете 10 заданий в очередь, а затем немедленно начинают использовать результаты. Не все работы будут завершены, и, возможно, ни одна из них еще не началась.

И при запуске в отладчике программа завершит работу, прежде чем вы увидите WrtieLine () из функции MyThreadFunction.

0 голосов
/ 19 декабря 2011

Разве это не значит, что когда вы создаете потоки, вам нужно присоединиться к ним (подождать, пока они не закончат), пока вы не попытаетесь напечатать что-нибудь "произведенное" ими?

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

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

...