控制台应用程序不遵守Thread.Join

时间:2013-08-11 15:31:08

标签: c# multithreading

我无法理解为什么我的控制台应用程序不会等到它产生的线程完全终止。我认为这与有问题的线程也会生成自己的子线程和/或包含System.Timer

这一事实有关。

基本程序流程如下。 Main为Simulator.Start方法创建一个新线程,然后加入直到该线程终止。 Simulator.Start创建一个新的Timer(以约束它应该执行多长时间),然后创建/运行一堆子线程。当Elapsed引发Timer事件时,这表示模拟器应终止其所有子线程并生成报告。问题是,一旦所有子线程终止并且生成报告的代码永远不会被执行,控制台应用程序就会退出(请参阅下面的Simulator.Stop方法)。

希望有些伪代码可以提供帮助:

public class Program
{
    private static Simulator _simulator;

    private static void Main(string[] args)
    {
        var options = new SimulationOptions();
        //check for valid options
        if (!Parser.Default.ParseArguments(args, options)) return;

        _simulator = new Simulator(options);

        var thread = new Thread(_simulator.Start) {IsBackground = false};
        thread.Start();
        thread.Join();
    }
}

public class Simulator
{
    private readonly SimulationOptions _options;
    private readonly List<Thread> _threads = new List<Thread>();
    private readonly List<Worker> _workers = new List<Worker>();
    private static Timer _timer;

    public Simulator(SimulationOptions options)
    {
        _options = options;
        StartTimer(_options.LengthOfTest);
    }

    private void StartTimer(int lengthOfTest)
    {
        _timer = new Timer {Interval = lengthOfTest*1000};
        _timer.Elapsed += Timer_Elapsed;
        _timer.Start();
    }

    private void Timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        _timer.Stop();
        Stop();
    }

    public void Stop()
    {
        // Request that the worker thread stop itself:
        foreach (Worker worker in _workers)
        {
            worker.RequestStop();
        }

        GenerateReport(); //<-- this code never gets executed
    }

    private XDocument GenerateReport()
    {
        //build an awesome report
    }

    public void Start()
    {
        _threads.Clear();
        _workers.Clear();
        for (int i = 0; i < _options.NumberOfClients; i++)
        {
            _workers.Add(new Worker());
            _threads.Add(new Thread(_workers.Last().PumpMessages));
            _threads.Last().Start();
        }
    }
}

public class Worker
{
    private bool _shouldStop = false;

    public void PumpMessages()
    {
        while (!_shouldStop)
        {
            //does cool stuff until told to stop
        }
    }

    public void RequestStop()
    {
        _shouldStop = true;
    }
}

2 个答案:

答案 0 :(得分:3)

start方法中没有任何内容可以使线程保持活动状态。当以下方法完成时,线程也是如此。然后你调用Thread.Join,那就是结束。

public void Start()
{
    _threads.Clear();
    _workers.Clear();
    for (int i = 0; i < _options.NumberOfClients; i++)
    {
        _workers.Add(new Worker());
        _threads.Add(new Thread(_workers.Last().PumpMessages));
        _threads.Last().Start();
    }
}

如果您打算等待此工作完成,请考虑为您正在使用的每个工作线程等待ManualResetEvent。

http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx http://msdn.microsoft.com/en-us/library/system.threading.waithandle.waitall.aspx

您的方法应如下所示。

public void Start()
{
    _threads.Clear();
    _workers.Clear();
    var evts = new List<ManualResetEvent>()
    for (int i = 0; i < _options.NumberOfClients; i++)
    {
        ManualResetEvent evt = new ManualResetEvent(false);
        evts.Add(evt);
        _workers.Add(new Worker(evt));
        _threads.Add(new Thread(_workers.Last().PumpMessages));
        _threads.Last().Start();
    }
    WaitHandle.WaitAll(evts.ToArray());
}

public class Worker
{
    private bool _shouldStop = false;
    private readonly ManualResetEvent @event;

    public Worker(ManualResetEvent @event)
    {
        this.@event = @event;
    }
    public void PumpMessages()
    {
        while (!_shouldStop)
        {
            //does cool stuff until told to stop
        }
        @event.Set();
    }
    public void RequestStop()
    {
        _shouldStop = true;
    }
}

答案 1 :(得分:2)

Join方法仅等待您加入的线程实例,因此Simulator.Start只创建一些线程并终止,因为结果Join返回并且您的主线程终止。但是你的应用程序仍然存在(原因是其他一些Foreground线程仍在运行)。

generate a report never gets executed? Why?

当所有Foreground Threads终止时,进程将终止。所以当你的子线程在循环中调用PumpMessages时从RequestStop方法返回时,所有前台线程都会终止

public void Stop()
{
    // Request that the worker thread stop itself:
    foreach (Worker worker in _workers)
    {
        worker.RequestStop();
    }
    <--here all foreground threads are ready to terminate
    GenerateReport(); //<-- this code never gets executed
}

我说all foreground threads die after the loop并没有误导。为了说清楚,我们已经说明了工作线程停止工作的指令,因此在执行GenerateReport方法之前,所有线程都可能会死亡,也可能不会死亡。是的,有一个Race如果工作线程赢得比赛,那么我们就失去它,反之亦然。有时你的GenerateReport可以毫无问题地执行。

如何解决?我们只是等待所有工作线程终止。就是这样。

public void Start()
{
    _threads.Clear();
    _workers.Clear();
    for (int i = 0; i < _options.NumberOfClients; i++)
    {
        _workers.Add(new Worker());
        _threads.Add(new Thread(_workers.Last().PumpMessages));
        _threads.Last().Start();
    }
    foreach (var t in _threads)
       t.Join();
}