C# 如何在按下按钮时启动线程并在再次按下时停止线程?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1481569/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to start thread if button pressed and stop it if pressed again?
提问by Dabbas
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 .. 所以每次用户输入一个字符时,代码将再次执行 .. 并且 BackgroundWorker 不起作用。
采纳答案by JaredPar
One problem to this approach is that it's verydangerous 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. The Threads in the ThreadPool are not owned by you and Aborting them cold have serious implications for the ThreadPool.
此外,在 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;
}
}
回答by Adam Robinson
Yes, this is very wrong. You should never try to manually control a ThreadPool
thread. If you need this sort of control, you should be using your own Thread
object. In addition, Abort()
is not the recommended way of ending a thread; 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
. 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. The ThreadPool
shouldn't be used for long-running processes.
是的,这是非常错误的。您永远不应该尝试手动控制ThreadPool
线程。如果你需要这种控制,你应该使用你自己的Thread
对象。此外,Abort()
不是推荐的结束线程的方式;您应该volatile bool
对表单进行控制,以便代码在MethodToCall
各个点进行检查并在true
. 虽然您可以对 使用相同的方法,但ThreadPool
您需要能够取消这一事实似乎表明该过程是长期运行的,或者至少有可能是长期运行的。该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. For example...
然后在 中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;
}
}
And just repeat that pattern.
只需重复该模式。
回答by Matt Davis
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. It could easily be made to do so. And as a suggestion, I wouldn't muck with SetMaxThreads() function on the ThreadPool. Just let the ThreadPool do its thing. It's been designed to be optimal for the way you use it.
请注意,此解决方案根本不使用 ThreadPool。这样做很容易。作为一个建议,我不会在 ThreadPool 上使用 SetMaxThreads() 函数。只需让 ThreadPool 做它的事情。它被设计为最适合您的使用方式。
回答by new bie
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();
}
}
}
}