使用.NET,x64,Console.WriteLine多线程输出为空

时间:2011-12-18 20:05:12

标签: c# multithreading console windows-console

.NET ThreadPool有a sample on MSDN。如果我运行此代码,输出完全不稳定,有时,我在控制台上得到一个完全空的输出。

如果我添加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 个答案:

答案 0 :(得分:1)

这显然是一个控制台应用程序 - 在排队所有工作项后,Run方法返回,程序立即退出。它不会等待你的工作项目完成。至少,在运行后添加Thread.Sleep以允许线程池完成。

正确的做法是使用ManualResetEvent个实例的数组,并等待每个工作线程都为Set。由于这是一个控制台应用程序,除非您使用WaitHandle.WaitAll修饰Main方法,否则您将无法使用[STAThread]

答案 1 :(得分:1)

我很清楚输出可以是空的。

您在队列中推送10个作业,然后立即开始使用结果。并非所有工作都已完成,甚至还没有任何工作开始。

当在调试器中运行时,程序将在您从MyThreadFunction中看到WrtieLine()之前终止。

答案 2 :(得分:0)

当您创建线程时,您必须加入它们(等到它们完成),直到您尝试打印由它们“生成”的任何内容?

如果没有,您依赖的调度程序可以在Run中的最后一个循环之前切换线程。如果没有,最后一个循环在之前执行任何线程甚至开始。

另一方面,Thread.Sleep允许调度程序立即切换到其他线程,这可能是您在内部Thread.Sleep没有发现问题的原因。