如果按下按钮,如何启动线程,如果再次按下则停止?

时间:2009-09-26 16:22:15

标签: c# multithreading

我正在使用下一个代码来做我要求的事情:

private delegate void CallerDelegate(object e);
CallerDelegate caler = new CallerDelegate(MethodToCall);
按钮点击事件

if (currBusyThrd != null && currBusyThrd.IsAlive)
   {
    currBusyThrd.Abort();
   }
ThreadPool.SetMaxThreads(1, 1);
//queue the work for thread processing
ThreadPool.QueueUserWorkItem(new WaitCallback(WaitCallbackMethod))

“WaitCallbackMethod”方法是:

void WaitCallbackMethod(object stateInfo)
  {
     // argList : i put some argument in a list to use it in "MethodToCall" ...
     BeginInvoke(caler,argList);
  }

我正在调用的方法是:

void MethodToCall(object args)
 {
 //Here I get the thread I'm calling to stop it when btn clicked again
 currBusyThrd = Thread.CurrentThread;

 // The rest of the code ...
 }

我觉得这是错的...... 怎么做对了?

实际上调用将由TextBox_KeyUp ..所以每次用户输入一个char时,代码将再次执行..而BackgroundWorker不起作用。

4 个答案:

答案 0 :(得分:10)

这种方法的一个问题是,任意中止一个线程(几乎任何语言)都非常危险。有太多的问题可以围绕不同意的资源和错误的锁定进行弹出。通常最好设置某种标志来要求线程安全地中止自己或忘记线程并让它运行完成。

此外,在ThreadPool中中止线程是非常危险的,我认为不支持操作。 ThreadPool中的线程不归您所有,并且冷却它们会对ThreadPool产生严重影响。

这是我要采取的解决方案。

private object m_lock = new object();
private bool m_isRunning = false;
private bool m_isAbortRequested = false;

public void OnButtonClick(object sender, EventArgs e) {
  lock ( m_lock ) {
    if ( m_isRunning ) {
      m_isAbortRequested = true;
    } else {
      m_isAbortRequested = false;
      m_isRunning = true;
      ThreadPool.QueueUserWorkItem(BackgroundMethod);
    }
  }
}

private void BackgroundMethod() {
  try {
    DoRealWork();
  } finally {
    lock (m_lock) {
      m_isRunning = false;
    }
  }
}

private void DoRealWork() {
  ...
  if ( m_isAbortRequested ) {
    return;
  }
}

答案 1 :(得分:3)

是的,这是非常错误的。您永远不应该尝试手动控制ThreadPool线程。如果需要这种控制,则应使用自己的Thread对象。另外,Abort()不是推荐的结束线程的方法;您的表单上应该有一个控件volatile boolMethodToCall中的代码会检查各个点,并在true时正常退出。虽然您可以对ThreadPool使用相同的方法,但您需要能够取消的事实似乎表明该过程是长期运行的,或者至少具有潜在的可能性。 ThreadPool不应用于长时间运行的进程。

例如......

private volatile bool stopThread = false;
private Thread workThread;

private void StartThread()
{
    if(workThread == null)
    {
        stopThread = false;
        workThread = new Thread(new ThreadStart(MethodToCall));

        workThread.Start();
    }
}

private void StopThread()
{
    if(workThread != null)
    {
        stopThread = true;

        workThread.Join(); // This makes the code here pause until the Thread exits.

        workThread = null;
    }
}

然后在MethodToCall中,只需频繁检查stopThread布尔值,并执行您需要执行的任何清理工作并退出方法。例如......

private void MethodToCall()
{
    // do some work here and get to a logical stopping point

    if(stopThread)
    {
        // clean up your work

        return;
    }

    // do some more work and get to another stopping point

    if(stopThread)
    {
        // clean up your work

        return;
    }
}

然后重复那种模式。

答案 2 :(得分:2)

对于一个线程需要“发出信号”另一个线程来执行某些操作的情况,我通常使用System.Threading.ManualResetEvent来指示辅助线程停止,如下所示:

private volatile bool _threadRunning = false;
private ManualResetEvent _signal = new ManualResetEvent(false);
private Thread _thread;
private void OnButtonClick(object sender, EventArgs e)
{
    if (!_threadRunning) {
        // Reset the 'signal' event.
        _signal.Reset();
        // Build your thread parameter here.
        object param = ;
        // Create the thread.
        _thread = new Thread(ExecuteThreadLogicConditionally(param));
        // Make sure the thread shuts down automatically when UI closes
        _thread.IsBackground = true;
        // Start the thread.
        _thread.Start();
        // Prevent another thread from being started.
        _threadRunning = true;
    } else {
        // Signal the thread to stop.
        _signal.Set();
        // DO NOT JOIN THE THREAD HERE!  If the thread takes a while
        // to exit, then your UI will be frozen until it does.  Just
        // set the signal and move on.
    }
}
// If the thread is intended to execute its logic over and over until
// stopped, use this callback.
private void ExecuteThreadLogicUntilStopped(object param)
{
    // Use a while loop to prevent the thread from exiting too early.
    while (!_signal.WaitOne(0)) {
        // Put your thread logic here...
    }
    // Set the flag so anther thread can be started.
    _threadRunning = false;
}
// If the thread logic is to be executed once and then wait to be
// shutdown, use this callback.
private void ExecuteThreadLogicOnce(object param)
{
    // Put your thread logic here...
    //
    // Now wait for signal to stop.
    _signal.WaitOne();
    // Set the flag so another thread can be started.
    _threadRunning = false;
}
// If the thread needs to be stopped at any point along the way, use
// this callback.  The key here is to 'sprinkle' checks of the 'signal'
// to see if the thread should stop prematurely.
private void ExecuteThreadLogicConditionally(object param)
{
    if (_signal.WaitOne(0)) { _threadRunning = false; return; }
    // Execute small chunk of logic here...
    if (_signal.WaitOne(0)) { _threadRunning = false; return; }
    // Execute another small chuck of logic here...
    if (_signal.WaitOne(0)) { _threadRunning = false; return; }
    // Continue this pattern through the method.
}

请注意,此解决方案根本不使用ThreadPool。很容易就可以这样做。作为一个建议,我不会在ThreadPool上使用SetMaxThreads()函数。让ThreadPool做它的事情。它被设计成最适合您使用它的方式。

答案 3 :(得分:1)

试试这段代码..

using System;
using System.Linq;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        Thread workerThread = null;
        ManualResetEvent threadInterrupt = new ManualResetEvent(false);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (this.workerThread == null)
            {
                this.threadInterrupt.Reset();
                this.workerThread = new Thread(() =>
                {
                    int i = 0;
                    while (!this.threadInterrupt.WaitOne(0))
                    {
                        Debug.Print("put your code in here while worker thread running.. " + i.ToString());
                        Thread.Sleep(100);
                        i++;
                    }
                    this.workerThread = null;
                    // worker thread finished in here..
                });
                this.workerThread.IsBackground = true;
                // start worker thread in here
                this.workerThread.Start();
            }
            else
            {
                // stop worker thread in here
                threadInterrupt.Set();
            }
        }

    }
}