I was trying out some QueuedBackgroundWorker class that I found here. It works well, except I'm wondering how I can wait till all queued workers have finished? For example, if the user goes to close the program I'd like the program to wait until all workers are finished and then close.
I tried doing something like this on the GUI thread, but it just seems to block:
try
{
while (myWorkerQueue.Queue.Count > 0) ;
}
catch (InvalidOperationException)
{
}
Also tried while(myWorkerQueue.Queue.Peek() != null) and got same result.
Code for QueuedBackgroundWorker:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
/// <summary>
/// This is thread-safe
/// </summary>
public class QueuedBackgroundWorker
{
#region Constructors
public QueuedBackgroundWorker() { }
#endregion
#region Properties
Queue<object> Queue = new Queue<object>();
object lockingObject1 = new object();
public delegate void WorkerCompletedDelegate<K>(K result, Exception error);
#endregion
#region Methods
/// <summary>
/// doWork is a method with one argument
/// </summary>
/// <typeparam name="T">is the type of the input parameter</typeparam>
/// <typeparam name="K">is the type of the output result</typeparam>
/// <param name="inputArgument"></param>
/// <param name="doWork"></param>
/// <param name="workerCompleted"></param>
public void RunAsync<T,K>(Func<T, K> doWork, T inputArgument, WorkerCompletedDelegate<K> workerCompleted)
{
BackgroundWorker bw = GetBackgroundWorker<T,K>(doWork, workerCompleted);
Queue.Enqueue(new QueueItem(bw, inputArgument));
lock (lockingObject1)
{
if (Queue.Count == 1)
{
((QueueItem)this.Queue.Peek()).RunWorkerAsync();
}
}
}
/// <summary>
/// Use this method if you don't need to handle when the worker is completed
/// </summary>
/// <param name="doWork"></param>
/// <param name="inputArgument"></param>
public void RunAsync<T,K>(Func<T, K> doWork, T inputArgument)
{
RunAsync(doWork, inputArgument, null);
}
private BackgroundWorker GetBackgroundWorker<T, K>(Func<T, K> doWork, WorkerCompletedDelegate<K> workerCompleted)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = false;
bw.WorkerSupportsCancellation = false;
bw.DoWork += (sender, args) =>
{
if (doWork != null)
{
args.Result = (K)doWork((T)args.Argument);
}
};
bw.RunWorkerCompleted += (sender, args) =>
{
if (workerCompleted != null)
{
workerCompleted((K)args.Result, args.Error);
}
Queue.Dequeue();
lock (lockingObject1)
{
if (Queue.Count > 0)
{
((QueueItem)this.Queue.Peek()).RunWorkerAsync();
}
}
};
return bw;
}
#endregion
}
public class QueueItem
{
#region Constructors
public QueueItem(BackgroundWorker backgroundWorker, object argument)
{
this.BackgroundWorker = backgroundWorker;
this.Argument = argument;
}
#endregion
#region Properties
public object Argument { get; private set; }
public BackgroundWorker BackgroundWorker { get; private set; }
#endregion
#region Methods
public void RunWorkerAsync()
{
this.BackgroundWorker.RunWorkerAsync(this.Argument);
}
#endregion
}
Do you absolutely have to use BackgroundWorker? .NET 4 introduces the Task API (aka Task Parallel Library or TPL in short): you can start several tasks and use Task.WhenAll to provide a continuation that only executes when all tasks are finished:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var myTasks = new List<Task<BitmapImage>>();
// Task<T>.Factory.StartNew starts the given method on a Thread Pool thread
myTasks.Add(Task<BitmapImage>.Factory.StartNew(LoadPicture1));
myTasks.Add(Task<BitmapImage>.Factory.StartNew(LoadPicture2));
// The important part: Task.WhenAll waits asynchronously until all tasks
// in the collection finished sucessfully. Only then, the lambda that is
// given to the ContinueWith method is executed. The UI thread does not block
// in this case.
Task.WhenAll(myTasks)
.ContinueWith(task =>
{
foreach (var bitmapImage in task.Result)
{
var image = new Image { Source = bitmapImage };
ImageStackPanel.Children.Add(image);
}
},
TaskScheduler.FromCurrentSynchronizationContext());
}
private BitmapImage LoadPicture1()
{
return LoadImageFile("Picture1.jpg");
}
private BitmapImage LoadPicture2()
{
// Simulate that this loading process takes a little bit longer
Thread.Sleep(1000);
return LoadImageFile("Picture2.jpg");
}
private BitmapImage LoadImageFile(string path)
{
using (var fileStream = new FileStream(path, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = fileStream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
}
You even can use async await if you program against .NET 4.5 (but I doubt that you use this version of .NET because you provided the .NET 4.0 tag in your question). Anyway, in case you want to stick to several BackgroundWorker objects, I would encapsulate all of them in a class and register to the Completed events of them. If all of them raised this event, then I would raise another event that tells all of them have completed.
You can learn more about the TPL here: https://msdn.microsoft.com/en-us/library/dd537609(v=vs.110).aspx
You can download the whole example that I created here: https://dl.dropboxusercontent.com/u/14810011/LoadSeveralItemsWithTasks.zip
If you do something like that
while (myWorkerQueue.Queue.Count > 0) ;
Your while loop is taking so much ressource that there is no more for your background threads. It appears blocked.
If you want to keep your while loop (which I don't advise), at least put a sleep in so your background threads can work:
while (myWorkerQueue.Queue.Count > 0)
System.Threading.Thread.Sleep(1000);
The easiest solution as you said in your comments is to hook the closing event and abort it if myWorkerQueue.Queue.Count > 0.
A more elegant solution is to create a modal form with a progress bar, show it when the form is closing and if myWorkerQueue.Queue.Count > 0, the progress bar would progress as the remaining background worker finish...
Related
I'm having problems with the invokerequired method from time to time and wanted to aks if someone knows that problem or can it explain to me. I have some code:
splashScreen splashObj = splashScreen.GetInstance();
if (thrd == null)
{
thrd = new Thread(new ThreadStart(loadingScreenStart));
thrd.IsBackground = true;
thrd.Start();
}
else
{
if (splashObj.InvokeRequired)
{
splashObj.Invoke((MethodInvoker)delegate()
{
splashObj.Show();
}
);
}
else
{
splashObj.Show();
}
}
if (splashObj.loadLabel.InvokeRequired)
{
splashObj.loadLabel.Invoke((MethodInvoker)delegate()
{
splashObj.loadLabel.Text = "Checking Username...";
}
);
}
else
{
splashObj.loadLabel.Text = "Checking Username...";
}
public void loadingScreenStart()
{
splashScreen splashObj = splashScreen.GetInstance();
Application.Run(splashScreen.GetInstance());
}
the splashscreen code:
public partial class splashScreen : Form
{
public Form1 form1;
public splashScreen()
{
InitializeComponent();
//form1 = frm1;
}
public splashScreen(Form1 frm1)
{
form1 = frm1;
}
private void splashScreen_Load(object sender, EventArgs e)
{
}
private static splashScreen m_instance = null;
private static object m_instanceLock = new object();
public static splashScreen GetInstance()
{
lock (m_instanceLock)
{
if (m_instance == null)
{
m_instance = new splashScreen();
}
}
return m_instance;
}
}
So I'm showing a splashscreen while the rest of the code is loading in the background. Messages are getting showed like "Checking Username...", "Checking Password...", "Loading..." and so on.
This code works fine but from time to time it seems like the code execution is faster than the thread and then I get an error that the method was called from an other thread than the one it was created on. Why does this happen? Is there any solution for this kind of problem? That happens maybe once in twenty executions.
Multi-threading is hard, so you should try making it as simple as possible. One, there's no reason to have any of the splashscreen code outside of the splashscreen class itself. Second, you should always know what you're doing, so InvokeRequired is just a code smell that says "I have no idea who's going to call this method".
void Main()
{
SplashScreen.ShowText("Loading 1");
Thread.Sleep(1000);
SplashScreen.ShowText("Loading 2");
Thread.Sleep(2000);
SplashScreen.Done();
Thread.Sleep(2000);
SplashScreen.ShowText("Loading 3");
}
// Define other methods and classes here
public partial class SplashScreen : Form
{
private static SplashScreen instance;
private static readonly ManualResetEvent initEvent = new ManualResetEvent(false);
Label loadLabel;
private SplashScreen()
{
// InitializeComponent();
loadLabel = new Label();
Controls.Add(loadLabel);
Load += (s, e) => initEvent.Set();
Closing += (s, e) => initEvent.Reset();
}
private static object syncObject = new object();
private static void InitializeIfRequired()
{
// If not set, we'll have to init the message loop
if (!initEvent.WaitOne(0))
{
lock (syncObject)
{
// Someone initialized it before us
if (initEvent.WaitOne(0)) return;
// Recreate the form if it was closed
instance = new SplashScreen();
var thread = new Thread(() => { Application.Run(instance); });
thread.Start();
// Wait until the form is ready
initEvent.WaitOne();
}
}
}
public static void ShowText(string text)
{
InitializeIfRequired();
instance.Invoke((Action)(() =>
{
if (!instance.IsDisposed) instance.loadLabel.Text = text;
}
));
}
public static void Done()
{
// Is it closed already?
if (!initEvent.WaitOne(0)) return;
lock (syncObject)
{
// Someone closed it before us
if (!initEvent.WaitOne(0)) return;
instance.Invoke((Action)(() => { instance.Close(); }));
}
}
}
This is a snippet for LINQPad, you'll want to remove the comment on InitializeComponent as well as the loadLabel control (which should be on the designer instead).
This way, you have the logic of the splash screen completely isolated from the rest of the application. To show a splashscreen text, just call SplashScreen.ShowText. To make the splashscreen go away, call SplashScreen.Done.
Note how there's no need to use InvokeRequired - there's no way any legitimate call of SplashScreen.ShowText (or Done) would ever not need marshalling to the UI thread of the splash screen.
Now, this is not perfect. It's something I wrote in about 10 minutes. But it's (probably :)) thread-safe, follows best practices a lot better than the original, and is much easier to use.
Also, there's cases where I would use higher-level constructs, e.g. Lazy<T> and Task - but since that wouldn't really help here (starting the new messaging loop, having to recreate the form if it was closed...), I opted for the simpler solution instead.
Note that I'm using Invoke rather than BeginInvoke or similar - this is quite important, because otherwise it would be possible to queue a close, followed by a ShowText that would work on a disposed form. If you intend to call ShowText from multiple threads, it would be safer to also lock around the whole ShowText body. Given the usual use case for splash screens, there's some thread-safety that's unnecessary, but...
Ok so this problem still exists, it can happen when your thread is calling the form before it shows; both InvokeRequired and BeginInvoke may fail.
A solution is to start your thread on form event Shown:
/// <summary>
/// Testing form
/// </summary>
public partial class MyForm : Form
{
/// <summary>
/// Work work
/// </summary>
private Thread _MyThread;
/// <summary>
/// New test form
/// </summary>
public MyForm()
{
// Initialize components
this.InitializeComponent();
}
/// <summary>
/// First time form is show
/// </summary>
private void MyForm_Shown(object sender, EventArgs e)
{
// Create and start thread
this._MyThread = new Thread(this.MyThreadWork);
this._MyThread.Start();
}
/// <summary>
/// Doing some work here
/// </summary>
private void MyThreadWork()
{
// Counting time
int count = 0;
// Forever and ever
while (true)
{
this.PrintInfo((count++).ToString());
Thread.Sleep(1000);
}
}
/// <summary>
/// Printing info into form title
/// </summary>
private void PrintInfo(string info)
{
// Needs to sync?
if (this.InvokeRequired)
{
// Sync me
this.BeginInvoke(new Action<string>(this.PrintInfo), info);
}
else
{
// Prints info
this.Text = info;
}
}
/// <summary>
/// Good bye form, don't miss the work work
/// </summary>
private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
{
// Kill worker
this._MyThread.Abort();
}
}
Some will just add a Thread.Sleep before first PrintInfo but common, what sleep time do you would use? 1, 10, 100? Depends on how fast your form will show.
I am creating an opc server with third party dll.they had given an example in which all functions r running on a different thread.
here is the example,OPCServer.cs:
public static OpcServer CreateInstanceAsync()
{
Thread thread = new Thread(new ParameterizedThreadStart(InitializationThread));
OpcServer opcServer = new OpcServer();
thread.Start(opcServer);
thread.Join();
return opcServer;
}
static void InitializationThread(object arg)
{
((OpcServer)arg).Initialize();
}
void Initialize()
{
//some stuff
}
public void UpdateValues(string[] n)
{
this.BeginUpdate();
value1 = (object[])n;
for (int i = 0; i < tag_count; i++)
{
this.SetTag(tag_ids[i], value1[i], Quality.Good, FileTime.UtcNow);
}
this.EndUpdate(false);
}
I am getting problem in the method UpdateValues();
in the main form:
public Form1()
{
InitializeComponent();
opcServer = OpcServer.CreateInstanceAsync();
opcServer.UpdateValues(valuesInArray);
}
there is a timer & the UpdateValues() method will call at every time tick with a new value. interval is 10 secs.
private void timer1_Tick(object sender, EventArgs e)
{
opcServer.UpdateValues(valuesInArray);
}
the program is running smoothly for some time. but after that it showing stack overflow exception.,sometimes pc got hanged.i don't understand why? how do i get rid from this? the OPCServer.cs is given by the 3rd party.my work is to passing value in that particular method.will i have to create a new thread each time i will call that method?
Try BackgroundWorker for updating form while running long process. Use ProgressChanged event to update the form values else invoke a delegate to update form controls.
Another alternative would be to use the Task Parallel Library and then use events and delegates to interact with form elements.
Using the Task Parallel Library is very easy:
foreach (DriveInfo info in DriveInfo.GetDrives())
{
if (info.DriveType == DriveType.Fixed)
{
var task = Task.Factory.StartNew(() => scanFiles(findType, info.RootDirectory.Name));
}
}
This would be an example of interacting with the form elements:
In my external class:
/// <summary>
/// Delegate for setting text box text
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void TextBoxEventHandler(object sender, TextEventArgs e);
/// <summary>
/// Event for changing tool bar text
/// </summary>
public event TextBoxEventHandler ChangeTextBoxText = delegate { };
/// <summary>
/// Function that raises set tool bar text event
/// </summary>
/// <param name="s"></param>
public void SetTextBoxText(string s)
{
ChangeTextBoxText(this, new TextEventArgs(s));
}
In my form:
scanner.ChangeTextBoxText += scanner_ChangeTextBoxText;
private void scanner_ChangeTextBoxText(object sender, FS.TextEventArgs e)
{
addMessage(e.Message);
}
delegate void SetTextCallback(string text);
private void addMessage(string message)
{
if (edtContents.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(addMessage);
this.Invoke(d, new object[] { message });
}
else
{
edtContents.Text += String.Format("{0}{1}", message, Environment.NewLine);
edtContents.SelectionStart = edtContents.Text.Length;
edtContents.ScrollToCaret();
}
}
first of all why do you create a thread here
public static OpcServer CreateInstanceAsync()
{
Thread thread = new Thread(new ParameterizedThreadStart(InitializationThread));
OpcServer opcServer = new OpcServer();
thread.Start(opcServer);
thread.Join();
return opcServer;
}
because probably i think, you just don't want to hang your main form creation once you got the OpcServer object, you are just using the same instance to call the UpdateValues() in a timer.
now as you are piling things up in this call . how many updates you are adding.
this.SetTag(tag_ids[i], value1[i], Quality.Good, FileTime.UtcNow);
There must be some method to remove tags which are Old/Obsolete.
Check for the API documentation for freeing up the objects
I'm trying to generate and dispatch (while having my UI Thread with progressRing.IsActive = true;), three List Objects on a BackgroundWorker and then transfer said list to the UI Thread, but I'm running into issues with...
Must create DependencySource on same Thread as the DependencyObject.
Resources, I've read
C# lock (MSDN Documentation)
Sending Arguments to Background Worker (Stackoverflow)
C# Background Worker (MSDN Documentation)
C# getting Background Worker to return a result (Stackoverflow)
Method BackgroundLogin() of Partial Class MainWindow
private void BackgroundLogin()
{
//process the form on UI Thread
this.LoginForm(FadeOut);
this.LoadingRing(FadeIn);
//Start a new Thread
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
//initialize custom class WorkerThread object to store/process a result
WorkerThread wt = new WorkerThread(this, txtEmailAddress.Text, txtPassword.Password);
//start the worker and send the object across.
worker.RunWorkerAsync(wt);
}
Method worker_DoWork of Partial Class MainWindow
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
//grab the object
WorkerThread wt = (WorkerThread)e.Argument;
//check if we can login
if (!wt.Login())
{
//cancel the thread
e.Cancel = true;
}
else
{
//load additional data
wt.UpdateAuthLbl(".. Loading New Data ..");
wt.LoadLists();
wt.UpdateAuthLbl(".. Data Loaded ..");
}
//pass the object back
e.Result = wt;
}
Method loadLists() of Class WorkerThread
/// <summary>
/// Load data into the list
/// </summary>
public void LoadLists()
{
this.gene_fact_list = db.loadGeneFactTable();
this.gene_trait_fact_list = db.loadGeneTraitFactTable(this.gene_fact_list);
this.category_trait_list = db.loadCategoryTraits();
}
Method worker_RunWorkerCompleted of Partial Class MainWindow, Object gl of Class GeneList
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//grab the finished object
WorkerThread wt = (WorkerThread) e.Result;
//work out if we are logged in
Boolean LoginFlag = !e.Cancelled && e.Error == null;
if (LoginFlag)
{
lblAuthentication.Content = ".. Loading Interface ..";
//pass the finished object into memory
this.gl = wt;
//reset the listbox
this.resetListBox();
}
this.LoginForm(LoginFlag);
}
Method resetListBox() and ListBoxItems
/// <summary>
/// Load the data for the form
/// </summary>
public void resetListBox()
{
if (this.gl.notNullOrEmpty())
{
this.ListBoxItems.Clear();
//begin compiling the mainTab
foreach (KeyValuePair<long, GeneNotesDataModel> kvp in this.gl.gene_fact_list)
{
this.ListBoxItems.Add(kvp.Value);
}
}
} //close function
//declare WPF list binding
private ObservableCollection<GeneNotesDataModel> _listBoxItems = new ObservableCollection<GeneNotesDataModel>();
/// <summary>
/// Control the listbox of rsid codes
/// </summary>
public ObservableCollection<GeneNotesDataModel> ListBoxItems
{
get { return _listBoxItems; }
set { _listBoxItems = value; }
}
XAML ListBox lstSnpCodes
<ListBox ItemsSource="{Binding ElementName=UI, Path=ListBoxItems}" Margin="6,38,0,60" BorderThickness="2" HorizontalAlignment="Left" Width="180" Name="lstSnpCodes" SelectionChanged="lstSnpCodes_SelectionChanged" KeyUp="OnKeyUpHandler" />
The line this.ListBoxItems.Add(kvp.Value); causes the Exception to occur (If I replace it with Debug.WriteLine(kvp.Value.getValueAsString()); it will run just fine). Any ideas on why I'm getting a DependencySource exception? and why isn't possible to transfer ListA from Slave Thread to Main Thread?
PasteBin Links expire in April 2013
Instead of trying to access the property directly across threads, try accessing the variable behind it.
In sample app:
private static List<object> _lst;
static void Main(string[] args)
{
Task tsk = new Task(() => _lst = new List<object>()); //task will create new thread if available and execute the Action.
tsk.Start(); //initiates task, which initiates new List<object> on new thread.
tsk.Wait(); //waits for task to complete. Note that this locks main thread.
_lst.Add(1); //Attempt to access _lst from main thread.
}
Specific to your example.
private ReadOnly object _lockMyList = new object();
private List _MyList;
public void SetMyList(List lst) //Use this to set list from other thread.
{
Lock(_lockMyList)
{
_MyList = lst;
}
//Invoke Property Changed event if needed for binding.
}
public List MyList
{
get
{
List lst = null;
Lock(_lockMyList)
{
lst = _MyList; //You might need to do _MyList.ToList() for true thread safety
}
return lst;
}
//Property setter if needed, but use accessor method instead for setting property from other thread
}
Also, you may want to look into SynchronizedCollection to see if it is more suitable for your needs.
I hope this helps.
I have this bit of legacy code that's been sitting in the code-base and being used for the last couple of years, recently we found that its method StopImmediately() doesn't stop it at all. I cant make head nor tail of how it works as I've done very little with threads or background workers.
I was wondering if any of the experienced threading fellas could tell me how to stop this confusing little beast from completing its task. Below is the complete class (sorry for the amount of code)
I cannot figure out how to cancel it .. thanks in advance for any help ..
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
namespace GPS2.BackgroundWorkerEx
{
public class QedBackgroundWorker
{
public QedBackgroundWorker() {}
Queue<object> Queue = new Queue<object>();
object lockingObject1 = new object();
private Thread currentThread;
public delegate void WorkerCompletedDelegate<K>(K result, Exception error);
public object Arguments { get; set; }
/// <summary>
/// doWork is a method with one argument
/// </summary>
/// <typeparam name="T">is the type of the input parameter</typeparam>
/// <typeparam name="K">is the type of the output result</typeparam>
/// <param name="inputArgument"></param>
/// <param name="doWork"></param>
/// <param name="workerCompleted"></param>
public void RunAsync<T,K>(Func<T, K> doWork, T inputArgument, WorkerCompletedDelegate<K> workerCompleted)
{
BackgroundWorker bw = GetBackgroundWorker<T,K>(doWork, workerCompleted);
Queue.Enqueue(new QueueItem(bw, inputArgument));
lock (lockingObject1)
{
if (Queue.Count == 1)
{
((QueueItem)this.Queue.Peek()).RunWorkerAsync();
//currentThread = System.Threading.Thread.CurrentThread;
}
}
}
/// <summary>
/// Use this method if you don't need to handle when the worker is completed
/// </summary>
/// <param name="doWork"></param>
/// <param name="inputArgument"></param>
public void RunAsync<T,K>(Func<T, K> doWork, T inputArgument)
{
RunAsync(doWork, inputArgument, null);
}
private BackgroundWorker GetBackgroundWorker<T, K>(Func<T, K> doWork, WorkerCompletedDelegate<K> workerCompleted)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = false;
bw.WorkerSupportsCancellation = true;
bw.DoWork += (sender, args) =>{
if (doWork != null)
{
args.Result = (K)doWork((T)args.Argument);
currentThread = System.Threading.Thread.CurrentThread;
}
};
bw.RunWorkerCompleted += (sender, args) =>{
if (workerCompleted != null)
{
workerCompleted((K)args.Result, args.Error);
}
Queue.Dequeue();
lock (lockingObject1)
{
if (Queue.Count > 0)
{
((QueueItem)this.Queue.Peek()).RunWorkerAsync();
}
}
};
return bw;
}
public void StopImmediately()
{
if (currentThread != null)
currentThread.Abort();
}
public bool IsBusy()
{
ThreadState state = currentThread.ThreadState;
bool res = true;
switch (state)
{
case ThreadState.Running:
res = true;
break;
default:
res = false;
break;
}
return res;
}
}
public class QueueItem{
public QueueItem(BackgroundWorker backgroundWorker, object argument)
{
this.BackgroundWorker = backgroundWorker;
this.Argument = argument;
}
public object Argument { get; private set; }
public BackgroundWorker BackgroundWorker { get; private set; }
public void RunWorkerAsync()
{
this.BackgroundWorker.RunWorkerAsync(this.Argument);
}
}
}
The typical way for a BackgroundWorker to support cancellation is for the DoWork event handler to periodically check for the BackgroundWorker's CancellationPending property:
var worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += (sender, args) =>
{
foreach (var thing in listOfThingsToDo)
{
if (worker.CancellationPending)
{
// Someone has asked us to stop doing our thing
break;
}
else
{
DoTheThing(thing);
}
}
};
Someone who wanted to cancel this BackgroundWorker, if it were running, would call
worker.CancelAsync();
which would cause it to set its CancellationPending property to true and cause the DoWork event handler that's currently running to break out of its loop after it finished processing the current thing. It wouldn't terminate immediately; the thing doing the work has to periodically ask "should I quit now or keep going?" Either way, the worker thread terminates gracefully.
The code written above tries to terminate the background worker immediately by aborting the thread. This is usually not the way that you want to do it. Ideally, this code should be changed so that the functions passed in as the doWork parameter regularly check for a cancellation request.
But the main reason that it doesn't work as written is that the currentThread member is not set until after the doWork function is invoked:
bw.DoWork += (sender, args) =>
{
if (doWork != null)
{
args.Result = (K)doWork((T)args.Argument);
currentThread = System.Threading.Thread.CurrentThread;
}
};
A call to StopImmediately() would see a null thread, because the DoWork callback is waiting for the function passed in as doWork to finish before it assigns the currentThread member.
They should be flipped as follows
bw.DoWork += (sender, args) =>
{
if (doWork != null)
{
currentThread = System.Threading.Thread.CurrentThread;
args.Result = (K)doWork((T)args.Argument);
}
};
and then it will indeed terminate uncleanly and (more or less) immediately. I recommend reading this MSDN article for an example of how to better approach supporting cancellation in a BackgroundWorker.
Hope this helps!
I need to design perfect worker thread method. The method must do the following:
1) extract something from queue (let's say a queue of string) and do something
2) stop and return when class is disposed
3) wait for some event (that queue is not empty) and do not consume cpu
4) run in separate thread
Main thread will add string to queue and signal thread method to continue and do the job.
I would like you to provide me the the template with required syncronization objects.
class MyClass, IDisposable
{
// Thread safe queue from third party
private ThreadSafeQueue<string> _workerQueue;
private Thread _workerThread;
public bool Initialize()
{
_workerThread = new Thread(WorkerThread).Start();
}
public AddTask(string object)
{
_workerQueue.Enqueue(object);
// now we must signal worker thread
}
// this is worker thread
private void WorkerThread()
{
// This is what worker thread must do
List<string> objectList = _workerQueue.EnqueAll
// Do something
}
// Yeap, this is Dispose
public bool Dispose()
{
}
}
Try something like this. instantiate with type string and give it a delegate to process your string:
public class SuperQueue<T> : IDisposable where T : class
{
readonly object _locker = new object();
readonly List<Thread> _workers;
readonly Queue<T> _taskQueue = new Queue<T>();
readonly Action<T> _dequeueAction;
/// <summary>
/// Initializes a new instance of the <see cref="SuperQueue{T}"/> class.
/// </summary>
/// <param name="workerCount">The worker count.</param>
/// <param name="dequeueAction">The dequeue action.</param>
public SuperQueue(int workerCount, Action<T> dequeueAction)
{
_dequeueAction = dequeueAction;
_workers = new List<Thread>(workerCount);
// Create and start a separate thread for each worker
for (int i = 0; i < workerCount; i++)
{
Thread t = new Thread(Consume) { IsBackground = true, Name = string.Format("SuperQueue worker {0}",i )};
_workers.Add(t);
t.Start();
}
}
/// <summary>
/// Enqueues the task.
/// </summary>
/// <param name="task">The task.</param>
public void EnqueueTask(T task)
{
lock (_locker)
{
_taskQueue.Enqueue(task);
Monitor.PulseAll(_locker);
}
}
/// <summary>
/// Consumes this instance.
/// </summary>
void Consume()
{
while (true)
{
T item;
lock (_locker)
{
while (_taskQueue.Count == 0) Monitor.Wait(_locker);
item = _taskQueue.Dequeue();
}
if (item == null) return;
// run actual method
_dequeueAction(item);
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
// Enqueue one null task per worker to make each exit.
_workers.ForEach(thread => EnqueueTask(null));
_workers.ForEach(thread => thread.Join());
}
}
What you are describing is best accomplished with the producer-consumer pattern. This pattern is most easily implemented with a blocking queue. If you are using .NET 4.0 then you can take advantage of the BlockingCollection class. Here is how I am seeing your code working. In the following example I am using a null value as sentinel for gracefully ending the consumer, but you could also take advantage of the CancellationToken parameter on the Take method.
public class MyClass : IDisposable
{
private BlockingCollection<string> m_Queue = new BlockingCollection<string>();
public class MyClass()
{
var thread = new Thread(Process);
thread.IsBackground = true;
thread.Start();
}
public void Dispose()
{
m_Queue.Add(null);
}
public void AddTask(string item)
{
if (item == null)
{
throw new ArgumentNullException();
}
m_Queue.Add(item);
}
private void Process()
{
while (true)
{
string item = m_Queue.Take();
if (item == null)
{
break; // Gracefully end the consumer thread.
}
else
{
// Process the item here.
}
}
}
}
I think you should consider using BackgroundWorker class, which may fit well to your needs.
Sounds like BlockingQueue is what you need.
You should take a look at the new .Net 4 System.Collections.Concurrent Namespace. Also this little example should help you to get a better understanding on how to use it.