I have a BackgroundWorker_DoWork method in my client application which sends a tcp message using NetworkComms.Net to a server and check for an answer.
The method for that last part is
NetworkComms.AppendGlobalIncomingPacketHandler
where I can verify if the answer has a certain type and if yes, invoke another method to handle the answer message itself.
What I want to do is to stop the BackgroundWorker from within the handler, but I can't figure out how. Any help is greatly appreciated, as I'm very new to Object-Oriented Programming and I'm probably missing something fundamental.
Here's the relevant piece of code:
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
if (backgroundWorker2.CancellationPending)
{
e.Cancel = true;
return;
}
string serverIP = entr_serverIP.Text;
int serverPORT;
int.TryParse(entr_serverPORT.Text, out serverPORT);
bool loop = true;
while (loop == true)
{
if (backgroundWorker2.CancellationPending)
{
e.Cancel = true;
return;
}
try
{
NetworkComms.SendObject("Message", serverIP, serverPORT, "status");
NetworkComms.AppendGlobalIncomingPacketHandler<string>("ReturnHere", DoSomething2);
}
catch(DPSBase.CommsException ex2)
{
MessageBox.Show(ex2.ToString());
e.Cancel = true;
return;
}
Thread.Sleep(100);
}
}
private static void DoSomething2(PacketHeader header, Connection connection, string message)
{
bool svAlarmSent = false;
while (svAlarmSent == false)
{
if (message == "KEYWORD")
{
string svInfo = connection.ConnectionInfo.RemoteEndPoint.ToString();
Form4 form4 = new Form4("KEYWORD", null, svInfo);
form4.Show();
svAlarmSent = true;
backgroundWorker2.CancelAsync();
loop = false;
}
}
}
The two last lines of the above code don't work because the CancelAsync method and the loop variable don't exist in that context.
The first step to fixing this is to enable cancellation from the DoSomething2 method. To do this it needs access to the backgroundWorker2 variable. This is a field (attribute), hence you can give it access by making the method non-static:
private void DoSomething2(PacketHeader header, Connection connection, string message)
The next step is to simple remove the access of the loop value from DoSomething2. The responsibility of this method is to signal the cancellation only. It is the job of the backgroundWorker2_DoWork method to respond to this cancellation.
In fact the loop variable doesn't even need to be set. Once CancelAsync is called the following conditional will be met:
if (backgroundWorker2.CancellationPending)
{
e.Cancel = true;
return;
}
By virtue of returning this code will break the while loop by itself.
Overall I would say that this isn't really the intended use of a BackgroundWorker though. Cancellation is supposed to be used to allow the user, or some operation, to signal that the background task should cancel the work and return without completing (if possible). In this case you are using cancellation to signal the succesful completion of the code. This works but is somewhat of an unintended use case.
I am not sure exactly what you are asking, but here is an easy example how to handle background workers.
private readonly bool _shouldStop;
public void Start()
{
Thread workerThread = new Thread(DoWork);
workerThread.IsBackground = true;
workerThread.Start();
}
public void DoWork()
{
while (!_shouldStop)
{
//work
}
}
public void RequestStop()
{
_shouldStop = true;
}
to start the worker just call Start() and to stop it call RequestStop()
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
Related
What is the best solution to quickly cancel long running processes inside background worker?
For example, we have such situation:
private void DoWork(object sender, DoWorkEventArgs e)
{
...
for (int i = 0; i < items; i++)
{
if (_worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
VeryLongRunningProcess();
}
}
}
private void VeryLongRunningProcess()
{
var a = Test();
var b = Test2();
Thread.Sleep(5000);
var c = Test3();
}
In such case, VeryLongRunningProcess() will be not finished on pressing cancel until he finished everything inside his body.
What to do in such cases?
I tried to pass (BackgroundWorker)sender to VeryLongRunningProcess() as param and inside this method check for CancellationPending, but i dont know is this correct way or not
If the problem is isolated your VeryLongRunningProcess from classes like the worker, you can use a Func as a parameter and leave outside your method the worker access
private void VeryLongRunningProcess(Func<bool> isCancelled)
{
var a = Test();
if (isCancelled())
{
return;
}
var b = Test2();
if (isCancelled())
{
return;
}
Thread.Sleep(5000);
var c = Test3();
}
Inside your method, you may check if you must cancel the operation as many times you need. And you can use the Func as a parameter in other methods like Test1, Test2... if any of them takes long time to finish.
Then, you invoke your method in this form:
VeryLongRunningProcess(() => _worker.CancellationPending);
As other people comment, maybe interesting use async/await.
UPDATE
Another way to do if you want choose the use or not of the cancellation:
private void VeryLongRunningProcess(Func<bool> isCancelled = null)
{
var a = Test();
// Or: isCancelled != null && isCancelled()
if (isCancelled?.Invoke() ?? false)
{
return;
}
// ...
}
Normally you should create long-running process as "async" method (public async Task or Task DoWork()) for resources destribution purposes. "CancelationToken" enables cooperative cancellation between threads, thread pool work items. Also it is possible to propagate a callback delegate that can be invoked when Cancellation Token cancelled or function is compleete.
i am trying to use a third party telnet library "active expert" for a basic telnet session.
in my UI code behind i have something like
private async void Button_Click(object sender, RoutedEventArgs e)
{
var ts = new TelnetService();
await ts.DoConnect(node);
}
and my TelnetService looks like this
public class TelnetService
{
private Tcp objSocket = new Tcp();
private NwConstants objConstants = new NwConstants();
public string Responses { get; set; }
private Timer timer1 = new Timer();
public TelnetService()
{
timer1.Elapsed += timer1_Elapsed;
timer1.Interval = 100;
timer1.Start();
}
void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (objSocket.ConnectionState == objConstants.nwSOCKET_CONNSTATE_CONNECTED)
{
if (objSocket.HasData())
{
Responses += objSocket.ReceiveString() + "\r\n";
}
}
}
public Task DoConnect(Node node)
{
return Task.Factory.StartNew(() =>
{
objSocket.Protocol = objConstants.nwSOCKET_PROTOCOL_TELNET;
objSocket.Connect(node.IP, 23);
while (true)
{
if ((Responses == null) || (!Responses.Contains(node.WaitString))) continue;
//do something
Responses = "";
break;
}
});
}
}
there are two important pieces of functionalities.
First in the timer1_Elapsed function which is process that will keeps on ruining and checks if there is data on socket, and if there is, it will append it to a string "Response". and i am using "timer" for it.
Second in the DoConnect function which will check the"Response" string for a certain input. for this i am using async await and Task.
in a nutshell first one accumulating the Response and Second one checking the Response.
Problem is that it looks like the timer code in general and
objSocket.ReceiveString()
line specifically is causing the UI thread to halt for several seconds. which means after clicking the button i cannot move my main form on the screen however the code is running in a separate thread.
i have tried using pure Thread for this but it didn't helped either.
update
instead of timer i am using a method AccumulateResponse
public static void AccumulateResponse()
{
while (true)
{
if (objSocket.ConnectionState == objConstants.nwSOCKET_CONNSTATE_CONNECTED)
{
if (objSocket.HasData())
{
Responses += objSocket.ReceiveString() + "\r\n";
}
}
}
}
and calling it like
var t = new Task(TelnetService.AccumulateResponse);
t.Start();
await TelnetService.DoConnect(node);
still no luck
The DoConnect isn't your problem. It is your Timer Elapsed Event handler.
The timer elapsed event is NOT asynchronous. Only the DoConnect is.
If there is no asynchronous version of ReceiveString() from your third party lib, then use Task.Run there as well inside of an async timer1_elapsed method.
I have a lock in my c# web app that prevents users from running the update script once it has started.
I was thinking I would put a notification in my master page to let the user know that the data isn't all there yet.
Currently I do my locking like so.
protected void butRefreshData_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(UpdateDatabase));
t.Start(this);
//sleep for a bit to ensure that javascript has a chance to get rendered
Thread.Sleep(100);
}
public static void UpdateDatabase(object con)
{
if (Monitor.TryEnter(myLock))
{
Updater.RepopulateDatabase();
Monitor.Exit(myLock);
}
else
{
Common.RegisterStartupScript(con, AlreadyLockedJavaScript);
}
}
And I do not want to do
if(Monitor.TryEnter(myLock))
Monitor.Exit(myLock);
else
//show processing labal
As I imagine there is a slight possibility that it might display the notification when it isn't actually running.
Is there an alternative I can use?
Edit:
Hi Everyone, thanks a lot for your suggestions! Unfortunately I couldn't quite get them to work...
However I combined the ideas on 2 answers and came up with my own solution. It seems to be working so far but I have to wait for the process to complete...
Ok this seems to be working, I broke out the Repopule Method into it's own class.
public static class DataPopulation
{
public static bool IsUpdating = false;
private static string myLock = "My Lock";
private static string LockMessage = #"Sorry, the data repopulation process is already running and cannot be stopped. Please try again later. If the graphs are not slowly filling with data please contact your IT support specialist.";
private static string LockJavaScript = #"alert('" + LockMessage + #"');";
public static void Repopulate(object con)
{
if (Monitor.TryEnter(myLock))
{
IsUpdating = true;
MyProjectRepopulate.MyProjectRepopulate.RepopulateDatabase();
IsUpdating = false;
Monitor.Exit(myLock);
}
else
{
Common.RegisterStartupScript(con, LockJavaScript);
}
}
}
In master I do
protected void Page_Load(object sender, EventArgs e)
{
if (DataPopulation.IsUpdating)
lblRefresh.Visible = true;
else
lblRefresh.Visible = false;
}
(Given that you are aware of the race condition for displaying this notification just after processing stopped.... )
You could switch to a CountdownEvent. This works similarly to a ManualResetEvent, but also provides CurrentCount and IsSet properies, which could be used to determine if something is being processed.
How about just setting a volaltile bool property somewhere that indicates an active lock, perhaps via callback method?
Explore Autoresetevents and ManualResetevents. You can have the spawned thread set the event and check the event in the main thread to display the message.
butRefreshData_Click()
{
lock(myLock)
{
if (isbusy) {/*tell user*/}
}
}
UpdateDatabase(object con)
{
lock(myLock)
{
if (isbusy) {/*tell user*/ return;}
else {isbusy = true;}
}
Updater.RepopulateDatabase();
lock(myLock)
{
isBusy = false;
}
}
Note: You should probably wrap UpdateDatabase in a try-finally to avoid isBusy from being stuck true if an exception is thrown.
As I imagine there is a slight
possibility that it might display the
notification when it isn't actually
running.
There will always be the possibility you send the "Working..." message and then immediately the job is finished. What you have should logically work.
public static void UpdateDatabase(object con)
{
if (Monitor.TryEnter(myLock))
{
System.Diagnostics.Debug.WriteLine("Doing the work");
Thread.Sleep(5000);
Monitor.Exit(myLock);
System.Diagnostics.Debug.WriteLine("Done doing the work");
}
else
{
System.Diagnostics.Debug.WriteLine("Entrance was blocked");
}
}
I have a form with 2 comboboxes on it. And I want to fill combobox2.DataSource based on combobox1.Text and combobox2.Text (I assume that the user has completed input in combobox1 and is in the middle of inputting in combobox2). So I have an event handler for combobox2 like this:
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
As far as building DataSource is time-consuming process (it creates a request to database and executes it) I decided that it's better to perform it in another process using BackgroundWorker. So there's a scenario when cmbDataSourceExtractor hasn't completed its work and the user types one more symbol. In this case I get an exception on this line
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues ); about that BackgroundWorker is busy and cannot perform several actions in the same time.
How to get rid of this exception?
CancelAsync doesn't actually abort your thread or anything like that. It sends a message to the worker thread that work should be cancelled via BackgroundWorker.CancellationPending. Your DoWork delegate that is being run in the background must periodically check this property and handle the cancellation itself.
The tricky part is that your DoWork delegate is probably blocking, meaning that the work you do on your DataSource must complete before you can do anything else (like check for CancellationPending). You may need to move your actual work to yet another async delegate (or maybe better yet, submit the work to the ThreadPool), and have your main worker thread poll until this inner worker thread triggers a wait state, OR it detects CancellationPending.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx
http://www.codeproject.com/KB/cpp/BackgroundWorker_Threads.aspx
If you add a loop between the CancelAsync() and the RunWorkerAsync() like so it will solve your problem
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
The while loop with the call to Application.DoEvents() will hault the execution of your new worker thread until the current one has properly cancelled, keep in mind you still need to handle the cancellation of your worker thread. With something like:
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
The Application.DoEvents() in the first code snippet will continue to process your GUI threads message queue so the even to cancel and update the cmbDataSourceExtractor.IsBusy property will still be processed (if you simply added a continue instead of Application.DoEvents() the loop would lock the GUI thread into a busy state and would not process the event to update the cmbDataSourceExtractor.IsBusy)
You will have to use a flag shared between the main thread and the BackgroundWorker, such as BackgroundWorker.CancellationPending. When you want the BackgroundWorker to exit, just set the flag using BackgroundWorker.CancelAsync().
MSDN has a sample: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancellationpending.aspx
MY example . DoWork is below:
DoLengthyWork();
//this is never executed
if(bgWorker.CancellationPending)
{
MessageBox.Show("Up to here? ...");
e.Cancel = true;
}
inside DoLenghtyWork :
public void DoLenghtyWork()
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
inside OtherStuff() :
public void OtherStuff()
{
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
What you want to do is modify both DoLenghtyWork and OtherStuff() so that they become:
public void DoLenghtyWork()
{
if(!bgWorker.CancellationPending)
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
public void OtherStuff()
{
if(!bgWorker.CancellationPending)
{
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
The problem is caused by the fact that cmbDataSourceExtractor.CancelAsync() is an asynchronous method, the Cancel operation has not yet completed when cmdDataSourceExtractor.RunWorkerAsync(...) exitst. You should wait for cmdDataSourceExtractor to complete before calling RunWorkerAsync again. How to do this is explained in this SO question.
My answer is a bit different because I've tried these methods but they didn't work. My code uses an extra class that checks for a Boolean flag in a public static class as the database values are read or where I prefer it just before an object is added to a List object or something as such. See the change in the code below. I added the ThreadWatcher.StopThread property. for this explation I'm nog going to reinstate the current thread because it's not your issue but that's as easy as setting the property to false before accessing the next thread...
private void combobox2_TextChanged(object sender, EventArgs e)
{
//Stop the thread here with this
ThreadWatcher.StopThread = true;//the rest of this thread will run normally after the database function has stopped.
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
all fine
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
Now add the following class
public static class ThreadWatcher
{
public static bool StopThread { get; set; }
}
and in your class where you read the database
List<SomeObject>list = new List<SomeObject>();
...
if (!reader.IsDbNull(0))
something = reader.getString(0);
someobject = new someobject(something);
if (ThreadWatcher.StopThread == true)
break;
list.Add(something);
...
don't forget to use a finally block to properly close your database connection etc. Hope this helps! Please mark me up if you find it helpful.
In my case, I had to pool database for payment confirmation to come in and then update WPF UI.
Mechanism that spins up all the processes:
public void Execute(object parameter)
{
try
{
var url = string.Format("{0}New?transactionReference={1}", Settings.Default.PaymentUrlWebsite, "transactionRef");
Process.Start(new ProcessStartInfo(url));
ViewModel.UpdateUiWhenDoneWithPayment = new BackgroundWorker {WorkerSupportsCancellation = true};
ViewModel.UpdateUiWhenDoneWithPayment.DoWork += ViewModel.updateUiWhenDoneWithPayment_DoWork;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerCompleted += ViewModel.updateUiWhenDoneWithPayment_RunWorkerCompleted;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerAsync();
}
catch (Exception e)
{
ViewModel.Log.Error("Failed to navigate to payments", e);
MessageBox.Show("Failed to navigate to payments");
}
}
Mechanism that does checking for completion:
private void updateUiWhenDoneWithPayment_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(30000);
while (string.IsNullOrEmpty(GetAuthToken()) && !((BackgroundWorker)sender).CancellationPending)
{
Thread.Sleep(5000);
}
//Plug in pooling mechanism
this.AuthCode = GetAuthToken();
}
Mechanism that cancels if window gets closed:
private void PaymentView_OnUnloaded(object sender, RoutedEventArgs e)
{
var context = DataContext as PaymentViewModel;
if (context.UpdateUiWhenDoneWithPayment != null && context.UpdateUiWhenDoneWithPayment.WorkerSupportsCancellation && context.UpdateUiWhenDoneWithPayment.IsBusy)
context.UpdateUiWhenDoneWithPayment.CancelAsync();
}
I agree with guys. But sometimes you have to add more things.
IE
1) Add this worker.WorkerSupportsCancellation = true;
2) Add to you class some method to do the following things
public void KillMe()
{
worker.CancelAsync();
worker.Dispose();
worker = null;
GC.Collect();
}
So before close your application your have to call this method.
3) Probably you can Dispose, null all variables and timers which are inside of the BackgroundWorker.
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 :
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 .
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. The Threads in the ThreadPool are not owned by you and Aborting them cold have serious implications for the 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. 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.
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...
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:
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.
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();
}
}
}
}