4

我无法理解为什么我的控制台应用程序不等到它产生的线程完全终止。我认为这与相关线程也产生自己的子线程和/或包含 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;
    }
}
4

2 回答 2

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;
    }
}
于 2013-08-11T15:49:52.340 回答
2

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

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方法之前死亡。是的,有一场比赛如果工作线程赢得比赛,那么我们就输了,反之亦然。有时您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();
}
于 2013-08-11T16:13:55.200 回答