c#如何正确地停止一个线程并重新启动它

本文关键字:一个 线程 重新启动 正确地 | 更新日期: 2023-09-27 17:52:50

我有一个方法,它做一些工作,并在它完成后调用一个方法。在这个新调用的方法中,我停止了线程。

但是,如果我停止线程,下面的所有行将不会被读取。有什么合适的方法来停止线程并重新启动它吗?

下面是我的代码片段:
public partial class Form1 : Form
{
    string strProvider;
    MySqlConnection sqlConn;
    MySqlCommand command;
    MySqlDataReader Reader;
    Thread workerThread;
    public Form1()
    {
        InitializeComponent();
        initializedb();
        workerThread = new Thread(shows);
        workerThread.Start();
        workerThread.IsBackground = true;
    public void shows()
    {
        string k;
        command.CommandText = "SELECT * FROM imageframe1";
        sqlConn.Open();
        Reader = command.ExecuteReader();
        while (Reader.Read())
        {
            k = (string)(Reader.GetValue(1));
            pictureBox1.Image = Image.FromFile(k);
            Thread.Sleep(3000);
        }
        sqlConn.Close();
        stopthread();
    }
    public void stopthread()
    {
        workerThread.Abort();
        //*************everything after this line wont run********
        MessageBox.Show("end");
        //Thread.Sleep(3000);
        //workerThread.Start();
    }

我在这里做的是一个图片幻灯片。所以我想停止线程,然后重新启动它,检查db是否有新的图像,然后重新运行整个程序而不是在里面循环会占用很多内存资源。但问题是,当我停止它,我不能运行任何东西之后。

谁能给初学者一些启发吗?有例子会对我理解很有帮助

c#如何正确地停止一个线程并重新启动它

这不是暂停程序的方法。如果您希望用户能够暂时暂停从阅读器读取数据,那么更好的处理方法是设置一些标志,让while循环在每次迭代时检查。永远不应该通过调用Thread.Abort直接操作线程。因此,你的"stopthread"方法应该看起来更像这样,而不是试图停止线程:

private object lockObject = new object();
private bool isPaused;
public void stopthread()
{
    lock (lockObject) 
    {
        isPaused = true;
    }
    //*************everything after this line wont run********
    MessageBox.Show("end");
}

现在,为了真正尊重这个暂停动作,你必须修改读数来检查这个标志。一种方法是检查它是否在每次从读取器读取的迭代中暂停,如果是,等待直到它变为未暂停:

public void shows()
{
    ...
    while (Reader.Read())
    {
        while (true) 
        {
            lock (lockObject) 
            {
                if (!isPaused)
                    break;
            }
            Thread.Sleep(1000);
        }
        k = (string)(Reader.GetValue(1));
        pictureBox1.Image = Image.FromFile(k);
        Thread.Sleep(3000);
    }
    ...
}

当然,您还需要一个"startThread"方法:

public void startThread() 
{
    lock (lockObject) 
    {
        isPaused = false;
    }
    MessageBox.Show("start");
}

这种方法的缺点是它使SQL连接保持打开状态,并且在暂停时绑定线程。另一种方法是记住处理了多少行,并在取消暂停时重新启动线程,从停止的地方开始。您可能希望以类似的方式停止线程——如果线程已暂停,检查标志并关闭连接,并在停止时进行清理。然后,当取消暂停时,您将不得不从一个新线程开始进程,以某种方式设法跳过您已经处理过的线程—可能涉及调整SQL。

最后要考虑的是使用线程池而不是手动创建线程,尽管对于WinForms应用程序来说,成本可能是无关紧要的。

这是一个关于c#线程的系列文章。真的,我是说真的。你可能想看看它,因为寻找解决方案时,处理线程不仅是找到一个解决方案,但最重要的是真正理解发生了什么除了;)

http://www.albahari.com/threading/

我奶奶总是说"不要乱动线程"…她是对的……

我建议查看ManualResetEvent类。您可以让线程等待UI线程发出信号,然后继续。ManualResetEvent类可以有两种状态:有信号或无信号。线程可以调用WaitOne(),它在状态变为有信号状态之前不会返回(或者在状态已经有信号状态时立即返回)。通过调用set()将其设置为有信号,并通过调用reset()将其重置为无信号:

public partial class Form1 : Form
{
    string strProvider;
    MySqlConnection sqlConn;
    MySqlCommand command;
    MySqlDataReader Reader;
    Thread workerThread;
    ManualResetEvent manualReset;
    public Form1()
    {
        InitializeComponent();
        initializedb();
        workerThread = new Thread(shows);
        workerThread.Start();
        workerThread.IsBackground = true;
        manualReset = new ManualResetEvent(true); // true for allow running
    }
    public void shows()
    {
        string k;
        command.CommandText = "SELECT * FROM imageframe1";
        sqlConn.Open();
        Reader = command.ExecuteReader();
        while (Reader.Read())
        {
            k = (string)(Reader.GetValue(1));
            pictureBox1.Image = Image.FromFile(k);
            Thread.Sleep(3000);
            manualReset.WaitOne(); // wait if reset
        }
        sqlConn.Close();
        stopthread();
    }
    public void pauseThread()
    {
        manualReset.ReSet();
    }
    public void continueThread()
    {
        manualReset.Set();
    }
}

然而,为什么不简单地在表单上放置一个计时器,并将间隔设置为3000毫秒,然后在UI线程中加载图像呢?看起来您只是从数据库中获取文件名。如果没有太多,你应该将它们预加载到一个列表中,并跟踪你的位置,而不是保持一个打开的连接和阅读器。1万个100个字符的字符串只占用大约200k的内存。