C# 优雅地关闭线程

C# 优雅地关闭线程,c#,multithreading,C#,Multithreading,在我正在开发的一个应用程序中,我有一个主窗体,它只是放在那里并显示日志数据,还有一个工作线程,它在循环中自主地完成工作 MyWorker worker = new MyWorker(); MainForm mainForm = new MainForm(); // Subscribe form to log event so log data gets displayed worker.Log += mainForm.Log; // Start the worker thread's Mai

在我正在开发的一个应用程序中,我有一个主窗体,它只是放在那里并显示日志数据,还有一个工作线程,它在循环中自主地完成工作

MyWorker worker = new MyWorker();
MainForm mainForm = new MainForm();

// Subscribe form to log event so log data gets displayed
worker.Log += mainForm.Log;

// Start the worker thread's MainLoop
new Thread(new ThreadStart(worker.MainLoop)).Start();

// Show the form (blocking)
Application.Run(mainForm);

// If we end up here, the form has been closed and the worker has to stop running            
worker.Running = false;
如您所见,无论何时关闭窗体,工作线程都应该停止。工人看起来像这样:

public class MyWorker
{
    public String Running { get; set; }

    public MyWorker()
    {
        Running = true;
    }

    public void MainLoop()
    {

        while (Running)
        {

            DoExtensiveWork1();
            if (!Running) return;

            DoExtensiveWork2();
            if (!Running) return;

            DoExtensiveWork3();
            if (!Running) return;

            DoExtensiveWork4();
            if (!Running) return;

            DoExtensiveWork5();         
            if (!Running) return;

            // We have to wait fifteen minutes (900 seconds) 
            // before another "run" can be processed
            for (int i = 0; i < 900; i++)
            {
                Thread.Sleep(1000);
                if (!Running) return;
            }
        }
    }
}
public class MyWorker
{
    private readonly ManualResetEvent _stopEvent = new ManualResetEvent(false);
    private readonly Action[] _workUnits;

    private bool Running
    {
        get { return !_stopEvent.WaitOne(0); }
    }

    public MyWorker()
    {
        _workUnits = new Action[]
        {
            DoExtensiveWork1,
            DoExtensiveWork2,
            DoExtensiveWork3,
            DoExtensiveWork4,
            DoExtensiveWork5
        };
    }

    public void Stop()
    {
        _stopEvent.Set();
    }

    public void MainLoop()
    {

        while (Running)
        {
            foreach (var workUnit in _workUnits)
            {
                workUnit();
                if (!Running) return;
            }           

            // We have to wait fifteen minutes (900 seconds) 
            // before another "run" can be processed
            if (_stopEvent.WaitOne(900000)) return;
        }
    }
}
公共类MyWorker
{
运行{get;set;}的公共字符串
公职人员()
{
运行=真;
}
公共void MainLoop()
{
(跑步时)
{
DoExtensiveWork1();
如果(!运行)返回;
DoExtensiveWork2();
如果(!运行)返回;
DoExtensiveWork3();
如果(!运行)返回;
DoExtensiveWork4();
如果(!运行)返回;
DoExtensiveWork5();
如果(!运行)返回;
//我们必须等15分钟(900秒)
//在处理另一次“运行”之前
对于(int i=0;i<900;i++)
{
睡眠(1000);
如果(!运行)返回;
}
}
}
}
正如您所看到的,我希望线程在连续工作操作之间切换时能够停止,但在操作中不能停止。当操作(
DoExtensiveWorkN
)完成时,其状态和结果将持久化到磁盘或数据库中,因此在操作进行时退出(例如,通过
Thread.Abort
)不是一个选项

然而,我发现我刚刚编写的这段代码令人反感,尤其是“等待循环”,它休眠一秒钟900次,以防止线程在检测到
正在运行之前闲置15分钟
被设置为
false

我希望能够在主循环完成一项工作后立即抛出某种事件来停止主循环


有人能给我指出正确的方向吗?或者如果因为我完全误解了线程而需要完全重写,请告诉我在哪里解释了这些原则?

您可以对单个任务的运行和15分钟等待循环进行整理

我建议或许可以使用如下方式:

public class MyWorker
{
    public String Running { get; set; }

    public MyWorker()
    {
        Running = true;
    }

    public void MainLoop()
    {

        while (Running)
        {

            DoExtensiveWork1();
            if (!Running) return;

            DoExtensiveWork2();
            if (!Running) return;

            DoExtensiveWork3();
            if (!Running) return;

            DoExtensiveWork4();
            if (!Running) return;

            DoExtensiveWork5();         
            if (!Running) return;

            // We have to wait fifteen minutes (900 seconds) 
            // before another "run" can be processed
            for (int i = 0; i < 900; i++)
            {
                Thread.Sleep(1000);
                if (!Running) return;
            }
        }
    }
}
public class MyWorker
{
    private readonly ManualResetEvent _stopEvent = new ManualResetEvent(false);
    private readonly Action[] _workUnits;

    private bool Running
    {
        get { return !_stopEvent.WaitOne(0); }
    }

    public MyWorker()
    {
        _workUnits = new Action[]
        {
            DoExtensiveWork1,
            DoExtensiveWork2,
            DoExtensiveWork3,
            DoExtensiveWork4,
            DoExtensiveWork5
        };
    }

    public void Stop()
    {
        _stopEvent.Set();
    }

    public void MainLoop()
    {

        while (Running)
        {
            foreach (var workUnit in _workUnits)
            {
                workUnit();
                if (!Running) return;
            }           

            // We have to wait fifteen minutes (900 seconds) 
            // before another "run" can be processed
            if (_stopEvent.WaitOne(900000)) return;
        }
    }
}
然后在下一个适当的点停止该过程:

Worker.Stop();
我建议使用

你可以通过跑步来完成你的工作,而不是使用睡眠,你只需将计时器设置为在15分钟内再次关闭即可

如果您想提前停止它,那么调用某种中止方法(类似于设置Running=true变量)来停止计时器

需要注意的是,每次计时器事件触发时,它都会启动一个新线程,因此您不必担心杀死后台线程。线程完成其处理运行,将计时器设置为在15分钟内运行,然后线程自然完成。如果您在等待期间中止,那么您只需删除计时器,无需进行更多清理。如果在运行期间中止,则让运行完成,并在结束时检查标志,不再启动计时器,然后线程完成


对于计时器,您需要将计时器设置为在进程结束时手动启动。另一种方法是让计时器每15分钟运行一次,但这意味着,如果处理时间为10分钟,则只需在下次运行前5分钟。如果超过15分钟,你可能会有麻烦。另外,手动重新启动计时器可以保证处理不会在另一个正在运行时重新启动。

计时器无法解决我遇到的主要问题:我不希望表单关闭时工作继续进行,也不希望添加大量代码来不断检查。@CodeCaster:啊,我误解了“因此,在操作正在进行时退出(例如,通过Thread.Abort)不是一个选项”意味着整个线程。我当时的想法是使用一系列操作,但Iridium击败了我。尽管如此,我仍然更喜欢15分钟等待的计时器。:)我可以更清楚地描述这一点。不过感谢您的建议。:-)