简体   繁体   English

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

[英]How to start thread if button pressed and stop it if pressed again?

I'm using the next code to do what I'm asking for : 我正在使用下一个代码来做我要求的事情:

private delegate void CallerDelegate(object e);
CallerDelegate caler = new CallerDelegate(MethodToCall);

on button click event : 按钮点击事件:

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

"WaitCallbackMethod" Method is : “WaitCallbackMethod”方法是:

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

and the method i'm calling by the thread is : 我通过线程调用的方法是:

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 ...
 }

I feel that this is wrong ... How to do it right ? 我觉得这是错的......怎么做对了?

Actually the calling will be by TextBox_KeyUp .. so every time the user enter a char the code will execute again .. and the BackgroundWorker didn't work . 实际上调用将由TextBox_KeyUp ..所以每次用户输入一个char时代码将再次执行..并且BackgroundWorker不起作用。

One problem to this approach is that it's very dangerous to arbitrarily Abort a thread (in pretty much any language). 这种方法的一个问题是任意中止一个线程(几乎任何语言)是非常危险的。 There are too many issues that can popup around unfreed resources and misheld locks. 有太多的问题可以围绕不同意的资源和错误的锁定进行弹出。 It's typically best to set some kind of flag to ask the Thread to safely abort itself or to forget about the thread and let it run to completion. 通常最好设置某种标志来要求线程安全地中止自己或忘记线程并让它运行完成。

Additionally, Aborting a Thread in the ThreadPool is very dangerous and I believe not a supported operation. 此外,在ThreadPool中中止线程是非常危险的,我相信不支持的操作。 The Threads in the ThreadPool are not owned by you and Aborting them cold have serious implications for the ThreadPool. ThreadPool中的线程不归您所有,并且冷却它们会对ThreadPool产生严重影响。

Here is the solution I would take. 这是我要采取的解决方案。

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

Yes, this is very wrong. 是的,这是非常错误的。 You should never try to manually control a ThreadPool thread. 你永远不应该尝试手动控制ThreadPool线程。 If you need this sort of control, you should be using your own Thread object. 如果需要这种控件,则应该使用自己的Thread对象。 In addition, Abort() is not the recommended way of ending a thread; 另外, Abort()不是结束线程的推荐方法; you should have a control volatile bool on your form that the code in MethodToCall checks at various points and exits gracefully when it's true . 你应该在你的表单上有一个控制volatile boolMethodToCall中的代码MethodToCall检查各个点,并在它true时正常退出。 While you can use the same approach with the ThreadPool , the fact that you need to be able to cancel seems to indicate that the process is long-running, or at least has the potential to be. 虽然您可以使用与ThreadPool相同的方法,但您需要能够取消的事实似乎表明该过程是长期运行的,或者至少有可能发生。 The ThreadPool shouldn't be used for long-running processes. ThreadPool不应该用于长时间运行的进程。

For example... 例如...

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

Then in MethodToCall , just check the stopThread boolean at frequent intervals and do any cleanup work that you need to do and exit the method. 然后在MethodToCall ,只需频繁检查stopThread boolean并执行您需要执行的任何清理工作并退出该方法。 For example... 例如...

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

And just repeat that pattern. 并重复这种模式。

For situations where one thread needs to 'signal' another thread to do something, I usually use a System.Threading.ManualResetEvent to signal the secondary thread to stop, like this: 对于一个线程需要“发信号”另一个线程来执行某些操作的情况,我通常使用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.
}

Note that this solution does not use the ThreadPool at all. 请注意,此解决方案根本不使用ThreadPool。 It could easily be made to do so. 很容易就可以这样做。 And as a suggestion, I wouldn't muck with SetMaxThreads() function on the ThreadPool. 作为一个建议,我不会在ThreadPool上使用SetMaxThreads()函数。 Just let the ThreadPool do its thing. 让ThreadPool做它的事情。 It's been designed to be optimal for the way you use it. 它被设计成最适合您使用它的方式。

Try this code.. 试试这个代码..

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

    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM