控制台应用程序不遵守线程.加入



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

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

启动方法中没有任何东西可以保持线程的活力。当以下方法完成时,线程也会完成。然后调用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.aspxhttp://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;
    }
}

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

generate a report never gets executed? Why?

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

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方法之前可能会死亡,也可能不会死亡。是的,有一场比赛。如果工人线程赢得了比赛,那么我们就输了,反之亦然。有时您的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();
}

最新更新