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

时间:2011-04-26 05:04:43

标签: c# winforms multithreading loops

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

但是如果我停止线程,则不会读取以下所有行。有没有正确的方法来阻止线程并重新启动它?

这是我的代码段:

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();
    }

我在这里做的是我正在做一个幻灯片的图片。所以我想停止线程并启动它,检查数据库的新图像并重新运行整个事情而不是在其中循环将使用很多记忆资源。但问题是当我停止它时,我不能在那之后运行任何东西。

任何人都可以为这里的初学者提供一些启示吗?有些例子对我来说真的很有用。

3 个答案:

答案 0 :(得分:2)

这是not the way to pause a program。如果您希望用户能够暂时暂停读取器的读取,better way将通过设置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。

最后要考虑的是使用thread pools而不是手动创建线程,但对于WinForms应用程序,成本可能无关紧要。

答案 1 :(得分:0)

这是一个关于C#线程的精彩系列。真的,我的意思是。你可能想看看它,因为在处理线程时找到解决方案不仅仅是寻找解决方案,而且最重要的是真正了解除了之外发生的事情;)

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

我的爷爷总是习惯说“不要乱用线程”......她说对了......

答案 2 :(得分:0)

我建议查看ManualResetEvent class。您可以让您的线程等待从UI线程发出信号,然后继续。 ManualResetEvent类可以有两种状态:信号或非信号。线程可以调用WaitOne(),在状态变为发出信号之前不会返回(如果状态已经发出信号,则立即返回)。您通过调用Set()将其设置为发出信号,并通过调用Reset()将其重置为nonsignalled:

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();
    }
}

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