A while ago we added Python scripting to a Wpf application using IronPython. At first it was only 'slave' in the sense that a script was invoked by a button click for instance and then just ran to completion returning control to Wpf. Later on we added 'master' scripting: the script runs in it's own thread, and controls the rest of the application. That was quite challenging but after a while and with help of existing SO content we got it working, seemingly. Never really used it though, until now, and unfortunately it turns out it does not work properly. Core cause is that although there are two seperate STA threads (the main Wpf one and one for the script), and hence two different Dispatcher instances, the main thread seems to get blocked because the script thread is in a loop waiting for the main thread to complete (in response to a button click processed on the script thread and starting events on the main thread). The whole point of using two threads with seperate ui windows was of course this wouldn't happen. What is going on?
update It is reproducable with minimal code, so I'm linking to that instead of posting pseudo-code here. While creating the code I found that when the window created by the script thread is not embedded (set MainWindow.hostedWin = false) the deadlock does not occur and everything behaves as expected.
in response to comments So there are 3 threads of concern coming into play. Let's call them Python, Ui and Process. Python starts Process and waits for it to complete. Process calls Invoke on Ui. Which shouldn't be doing anything at that point: after all, it's Python that is blocking, not Ui, and the whole point of this construction is that Ui shouldn't have to interact with Python. Well, except that it does somehow. Which is the culprit. In the deadlock, Ui sits at PresentationFramework.dll!System.Windows.Interop.HwndHost.OnWindowPositionChanged(System.Windows.Rect rcBoundingBox) + 0x82 bytes and Process sits at WindowsBase.dll!System.Windows.Threading.DispatcherOperation.DispatcherOperationEvent.WaitOne() + 0x2f bytes and Python is just at Thread.Sleep.
What is going on here, and how to fix it?
I'll keep it short, very few odds that this answer is going to make you happy. It is a three-way deadlock. The most severe one in the interaction between the main thread and PythonThread. This deadlock occurs in the Windows kernel, the NtUserSetWindowPos() call cannot progress. It is blocked, waiting for the WM_LBUTTONUP callback notification on the PythonThread to finish running.
This deadlock is caused by your WpfHwndEmbedHost hack. Turning a top-level window owned by another thread or process into a child window is an appcompat feature that was meant to support Windows 3.x programs. A Windows version that did not yet support threads and where having one task embedding another task's window wasn't a problem. A WPF window isn't exactly much like such a window, to put it mildly. Otherwise a well-known troublemaker, for one the reason that embedding Acrobat Reader in a browser window works so very poorly. Not turning on the WS_CHILD style flag ought to bring relief, but WPF isn't happy about that. Simply setting hostedWin to false solves the problem.
The other deadlock is the one I warned you about, the interaction between the main thread and the ProcessThread. Dispatcher.Invoke() is dangerous, it deadlocks because the main thread is stuck in the kernel. Using Dispatcher.BeginInvoke() solves the problem. Partly, you still have the main thread go catatonic for 5 seconds.
The most severe problem is the kernel lock, that's going to bite in many other ways. You are going to have to keep it a separate window to avoid it. Not good news, I'm sure.
This is a long shot but you might have to implement your own SynchronizationContext to achieve this.
As far as I understand from Andrew Nosenko's answer and his references, it seem that CLR has its own mind about the message pump for the UI thread and it's not actually possible to run two UI threads under one window (BTW, I was able to replicate the issue without IronPython, that seems to be irrelevant here)
Main reference is from cbrumme's WebLog 'Apartments and Pumping in the CLR'
I keep saying that managed blocking will perform “some pumping” when called on an STA thread. Wouldn’t it be great to know exactly what will get pumped? Unfortunately, pumping is a black art which is beyond mortal comprehension. On Win2000 and up, we simply delegate to OLE32’s CoWaitForMultipleHandles service. And before we wrote the initial cut of our pumping code for NT4 and Win9X, I thought I would glance through CoWaitForMultipleHandles to see how it is done. It is many, many pages of complex code. And it uses special flags and APIs that aren’t even available on Win9X.
I must admit I am a little out of my depth here, and might be missing the point completely, so apologies upfront if this might not be an answer to the question at all (Nevertheless it's been a good experience for me, that's for sure).
I have tried using Andrew Nosenko's SynchronizationContext implementation to come up with an example unfortunately without success. Hope it helps you and good luck!
I have delt with similiar problems in my applications where i invoked some UI updates from my heavy load threads and had the same result the UI blocked the thread. I made a solution which i now use in every application though you would need to apply it to your application it works like this:
Apart from the thread you do your work in (and the UI thread) you also need to create another thread, this one will take data from a stack and send it to the UI thread.
Essentialy when you want your UI get a update by your work thread you save the result of your work thread into a List if its more complex data then you would need to create a struct and save all the current data from the thread into the struct and add it to the List (adding data to to the list doesnt require invoke).
Now your second threads runs in a loop and checks at a certain interval if there is something in the List if there is he adds the list elements to your UI.
Here is a sample of how it should work
using System;
using System.Windows;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
namespace nonblock
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private ListBox l1;
private ListBox l2;
private Thread workThread;
private Thread nonBlockThread;
private List<TwoNumbers> list;
private void Form1_Load(object sender, EventArgs e)
{
this.Size = new Size(500,500);
this.FormClosing += (ss, ee) =>
{
workThread.Abort();
nonBlockThread.Abort();
};
l1 = new ListBox();
l1.Dock = DockStyle.Left;
l2 = new ListBox();
l2.Dock = DockStyle.Right;
list = new List<TwoNumbers>();
this.Controls.Add(l1);
this.Controls.Add(l2);
workThread = new Thread(work);
workThread.Start();
nonBlockThread = new Thread(update);
nonBlockThread.Start();
}
private void work()
{
int a = 0;
int b = 0;
int counter = 0;
Random r = new Random();
while (true)
{
a += r.Next();
b += r.Next();
counter++;
if (counter % 10 == 0)
list.Add(new TwoNumbers(a, b));
Thread.Sleep(40);
}
}
private void update()
{
while (true)
{
if (list.Count > 0)
{
for (int a = 0; a < list.Count; a++)
{
l1.Invoke((MethodInvoker)(() => l1.Items.Add(list[0].n1)));
l2.Invoke((MethodInvoker)(() => l2.Items.Add(list[0].n2)));
list.RemoveAt(0);
}
}
Thread.Sleep(1000);
}
}
public class TwoNumbers
{
public int n1 { get; set; }
public int n2 { get; set; }
public TwoNumbers(int a, int b)
{
n1 = a;
n2 = b;
}
}
}
}
Related
EDIT:
please see question history, for unchanged question in order not to invalidate comments.
I am clicking button that executes certain codes and it creates a thread (System.Threading.Thread). When I reclick button which starts process it hangs and freezes ui. What could be the reason?
public partial class ucLoader : UserControl
{
//lock object for whole instance of class ucLoader
private object lockUcLoader = new object();
//bringing info from ui
private void btnBringInfo_Click(object sender, EventArgs e)
{
lock (lockUcLoader)
{
btnBringInfo_PerformClick(false);
}
}
//using this method because it could be called when even button not visible
internal void btnBringInfo_PerformClick(bool calledFromBandInit)
{
lock (lockUcLoader) //HANGS HERE when called multiple times and ui freeze as well
//by the way I am using (repetitive) lock, because this method also called independently from btnBringInfo_Click
{
//...
this.btnLoad_PerformClick();
}
}
//Another button perform click that could be triggered elsewhere when even button not visible
private void btnLoad_PerformClick()
{
lock (lockUcLoader) //I am using (repetitive) lock, because this method also called independently from btnBringInfo_PerformClick
{
//...
Run();
}
}
//method for creating thread which System.Threading.Thread
private void Run()
{
lock (lockUcLoader) //Maybe this lock is NOT REQUIRED, as it is called by only btnLoad_PerformClick(), could you please confirm?
{
//some code that thread can be killed when available, you can ingore this two lines as they are irrelevant to subject, I think
Source = new CancellationTokenSource();
Token = Source.Token;
var shell = new WindowsShell();
Thread = new Thread((object o) =>
{
//...
var tokenInThread = (CancellationToken)o;
exitCode =TaskExtractBatchFiles(cls, shell, exitCode);
using (var logEnt = new logEntities())
{
//Do some db operation
//...
this.Invoke((MethodInvoker)delegate
{
//do some ui update operation
//...
});
}
}
Thread.Start(Token);
}
}
public void Progress(string message)
{
Invoke((MethodInvoker)delegate //ATTENTION HERE see below picture Wait occurs here
{
if (message != null && message.Trim() != string.Empty)
{
this.txtStatus.AppendText(message + Environment.NewLine);
}
});
}
}
In order to avoid get closed question, what my question is how can I prevent
below method can be accesses with out lock from background thread and ui thread
public void Progress(string message)
{
Invoke((MethodInvoker)delegate //ATTENTION HERE see below picture Wait occurs here
{
if (message != null && message.Trim() != string.Empty)
{
this.txtStatus.AppendText(message + Environment.NewLine);
}
});
}
Invoke((MethodInvoker)delegate ...
Whenever you use the lock statement in your code then you always run the risk of inducing deadlock. One of the classic threading bugs. You generally need at least two locks to get there, acquiring them in the wrong order. And yes, there are two in your program. One you declared yourself. And one you cannot see because it is buried inside the plumbing that makes Control.Invoke() work. Not being able to see a lock is what makes deadlock a difficult problem to debug.
You can reason it out, the lock inside Control.Invoke is necessary to ensure that the worker thread is blocked until the UI thread executed the delegate target. Probably also helps to reason out why the program deadlocked. You started the worker thread, it acquired the lockUcLoader lock and starts doing its job, calling Control.Invoke while doing so. Now you click the button before the worker is done, it necessarily blocks. But that makes the UI thread go catatonic and no longer capable of executing the Control.Invoke code. So the worker thread hangs on the Invoke call and it won't release the lock. And the UI thread hangs forever on the lock since the worker can't complete, deadlock city.
Control.Invoke dates from .NET 1.0, a version of the framework that has several serious design mistakes in code related to threading. While meant to be helpful, they just set death-traps for programmers to blunder into. What is unique about Control.Invoke is that it is never correct to use it.
Distinguish Control.Invoke and Control.BeginInvoke. You only ever need Invoke when you need its return value. Note how you don't, using BeginInvoke instead is good enough and instantly solves the deadlock. You'd consider Invoke to obtain a value from the UI so you can use it in the worker thread. But that induces other major threading issue, a threading race bug, the worker has no idea what state the UI is in. Say, the user might be busy interacting with it, typing a new value. You can't know what value you obtain, it will easily be the stale old value. Inevitably producing a mismatch between the UI and the work being done. The only way to avoid that mishap is to prevent the user from typing a new value, easily done with Enable = false. But now it no longer makes sense to use Invoke, you might as well pass the value when you start the thread.
So using BeginInvoke is already good enough to solve the problem. But that is not where you should stop. There is no point to those locks in the Click event handlers, all they do is make the UI unresponsive, greatly confuzzling the user. What you must do instead is set the Enable properties of those buttons to false. Set them back to true when the worker is done. Now it can't go wrong anymore, you don't need the locks and the user gets good feedback.
There is another serious problem you haven't run into yet but you must address. A UserControl has no control over its lifetime, it gets disposed when the user closes the form on which it is hosted. But that is completely out of sync with the worker thread execution, it keeps calling BeginInvoke even though the control is dead as a doornail. That will make your program bomb, hopefully on an ObjectDisposedException. A threading race bug that a lock cannot solve. The form has to help, it must actively prevent the user from closing it. Some notes about this bug in this Q+A.
For completeness I should mention the third most common threading bug that code like this is likely to suffer from. It doesn't have an official name, I call it a "firehose bug". It occurs when the worker thread calls BeginInvoke too often, giving the UI thread too much work to do. Happens easily, calling it more than about thousand times per second tends to be enough. The UI thread starts burning 100% core, trying to keep up with the invoke requests and never being able to catch up. Easy to see, it stops painting itself and responding to input, duties that are performed with a lower priority. That needs to be fixed the logical way, updating UI more than 25 times per second just produces a blur that the human eye can't observe and is therefore pointless.
I have two threads that look like this
pbDB_running = true; // start progress bar
Thread connectDb = new Thread(new ThreadStart(ConnectToDb));
Thread runProgress = new Thread(new ThreadStart(RunpbDB));
connectDb.Start();
runProgress.Start();
connectDb.Join(); //wait untill the connection is done
pbDB_running = false; //stop the progress bar
as you probably might have guessed, ConnectToDb is used to make a connection to a database, while runpbDB is making a progress bar run on the interface. The progress bar (pbDB) is a Windows.Forms control created with drag and drop on the design view.
The runProgress thread is running RunpbDB() wich looks like this :
private void RunpbDB()
{
while (pbDB_running)
{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;
}
pbDB.Value = 0;
}
When the two threads start I get the following exception inside RunpbDB() :
Cross-thread operation not valid: Control 'pbDB' accessed from a thread other than the thread it was created on.
What can I do to overcome this situation?
Have you thought about using a BackgroundWorker? This might make your life a lot easier. You could set two up, one for your database call and the other for your progress bar. Just listen for the background workers ProgressChanged and RunWorkerCompleted events.
More information on MSDN
Use Control.Invoke method to remedy this problem. The whole your solution will became
private void RunpbDB()
{
while (pbDB_running)
{
Invoke((Action)(()=>{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;}));
}
Invoke((Action)(()=>{pbDB.Value = 0;});
}
You can use something along the lines of pbDB.InvokeRequired and if so, call pbDB.Invoke to perform your action back on the UI thread.
You do not need the check if you know it will always be done on a separate thread than the UI thread.
Here is a link to some code on this and other ways to accomplish this.
You could also use a BackgroundWorker
This is a security imposed by microsoft for its .NET technology. It basically happens when you access a winforms element from a separate thread, i.e. not in the main thread where the GUI winforms is running. The solution is to create a delegate for your RunpbDB method. See the solution here Best Way to Invoke Any Cross-Threaded Code?. in here too: How to update the GUI from another thread in C#?
Just make your life easier and use a BackgroundWorker for this if you don't have access to .NET 4.0. If you can use 4.0+, use the TPL. And if you can use 4.5, you can use the new async/await functionality. There are tons of examples here on Stack Overflow. Here is a link from Stephen Cleary comparing them.
Cross thread operation call when UI thread is involved was discouraged by VS team in 2.0(before that it was possible for security reasons. There are two ways to overcome this issue. Easy way is to set the static property
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls
to false which then disables this check globally from your application. But this solution is not advised by any one and not even by me since it again opens the security holes.
Another ways is, in the method first check if UI control needs Invoke,If so then use control's invoke method to invoke the current method again and then return. Code can better clear what I want to say so
private void RunpbDB()
{
if (pbDB.InvokeRequired)
{
pbDB.Invoke(new Action(RunpbDB));
return;
}
while (pbDB_running)
{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;
}
pbDB.Value = 0;
}
I'm novice in program with c#. I want to create thread that move label in the main UI without stuck the UI until the movement done
I built something but it didnt work
tell me what is my problem
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(Movelb);
t.IsBackground = true;
t.Start();enter code here
}
private void DOsomeThing()
{
label2.Visible = true;
label2.Location = new Point(0, 205);
for (int i = 0; i < 533; i++)
{
label2.Location = new Point(i, 205);
Thread.Sleep(10);
}
label1.Text="false";
}
private void Movelb()
{
if (this.InvokeRequired)
{
threadDel d = new threadDel(DOsomeThing);
this.BeginInvoke(d);
}
else
DOsomeThing();
}
Do not use threads to paint to forms or modify/update form contents. The recommended paradigm in Windows programming is One Thread Per Form or Window. If you want to create forms that run from separate threads, then you must
create the new thread first
create the Form on the new thread
In this way, the new thread will serve as the new Form's message handler. But even then, you should still do all manipulation of the Form within that thread (and if the form wants to modify contents in another form running on a different thread, then some additional thread-safe communication trickery may be required).
To animate window contents, you should use System.Windows.Forms.Timer instead, which executes on the Form's thread in lock-step with its other messages. You'll need to re-implement your animation as a state machine rather than a for() loop construct, though. That means the variables for Label position will need to be embedded into the Form class, so that updates can be preserved across Timer message invocations.
You need to understand the event model first. In event-driven environments like Windows or Android or Linux etc... the "automatic " tasks such as animations of coordinates or other properties are usually done using Timers that keep re-sending events back to the handler that advances the animation/process. In your particular example - if you need to move label, use Widows.Forms.Timer. It is not appropriate to block UI thread that processes events with lengthy tasks as UI thread will stall and your app will freeze or become jerky. NOW, on the other hand there are many cases when adding extra threads DOES help a lot, when? Not in your case, because you only change the coordinate of the label that is nothing in terms of CPU in comparison to repaint, so your solution with extra thread is LESS efficient and much more complex than using timer. An extra thread is beneficial only when the logical work it performs on animation model is comparable or out-weights the paint work- imagine a game where 200 bugs need to be animated on screen according to many logical rules, in this case bug painting may be done in UI thread, but bug property changes/animations may be done in another thread if those computations are intense.
How Events work?
An OS has an infinite loop inside that gets interrupted by keyboard, mouse and other events but the loop spins indefinitely until you shut down Windows (or Android or XWidnws...). At the end of the loop the OS looks at "raw" mouse/key events and dispatches them into appropriate application queue. It knows it by inspecting every app windows list, who is on top and thus it knows what window/app was under such and such X,Y mouse coordinate. When event gets dispatched to your app your job is to handle it very fast and look for another event in your queue (queues are bound to UI Threads/Windows).
How Timers Work?
A timer is a special kind of event that OS can keep sending to you periodically from its internal "infinite loop". OS keeps track of what apps requested to be notified and how often - when time comes, it adds a WM_TIMER(on MS Windows) into your windows queue. This way you don't block anything, but get a method in your code that gets called every X milliseconds. When you use .NET Timer class - it is just a wrapper around CreateTimer() KillTimer() (I dont recall exact func names) in Windows User APIs. .NET Timer also knows how to swallow the WM_TIMER and call a C# event/delegate for you.
I hope this helps!
Your code does nothing useful. It just starts a new background thread, which, in turn, invokes a delegate, being executed at the same UI thread, which had started... the background thread.
In other words, you can't move the label in worker thread, because moving the label brings to repainting, which can't be done from background thread.
I also had an idea of doing some work in a thread - and while this hard job
was carried out... the main-gui-form should be modified, so the user will
spot a progress.
Did some lookup and went into "delegates", "eventhandlers", and "very advanced pieces of code".
It took me some time to fix, and I came up with this very simple example. Have a look.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ProcessingUI
// You will find a form with "button1": will do some work in a seperate thread, and
// therefore you are allowed to do action in main-gui-form while this work is done,
// due to async. operation.
// While work is done in seperate thread - the main-gui-form will have a label modified...
// having the steps: 1,2,3,0.
// Also found... "button2": will do some work in same thread as gui, and
// therefore you are not allowed to do action in main-gui-form while this work is done,
// due to sync. operation (only one thread is established).
// While work is done in one-and-only-thread - the main-gui-form will have a label modified...
// having the steps: 1,2,3,0.
{
public delegate void UpdateTextDelegate();
public partial class Form1 : Form
{
public delegate void SetStatusText(string statusText);
public SetStatusText mySetStatusTextDelegate;
public Form1()
{
InitializeComponent();
mySetStatusTextDelegate = new SetStatusText(SetStatusTextMethod);
}
private void button1_Click(object sender, EventArgs e) // do work from new thread.
{
Worker w = new Worker(this);
Thread thread1 = new Thread(new ThreadStart(w.DoWork));
thread1.Start();
}
private void button2_Click(object sender, EventArgs e) // do work from local class - form is locked during 1-3 steps.
{
SetStatusTextMethod("1");
Thread.Sleep(3000);
SetStatusTextMethod("2");
Thread.Sleep(3000);
SetStatusTextMethod("3");
Thread.Sleep(3000);
SetStatusTextMethod("0");
}
public void SetStatusTextMethod(string statusText)
{
label1.Text = statusText;
label1.Refresh();
}
}
public class Worker
{
Form1 guiForm; // holds form where "control-to-be-changes" is found.
public Worker(Form1 _guiForm)
{
guiForm = _guiForm;
}
public void DoWork() // while steps are being done - form can easily be moved around... is not locked!
{
// put "1/3" on form.
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "1");
Thread.Sleep(3000);
// put "2/3" on form.
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "2");
Thread.Sleep(3000);
// put "3/3" on form.
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "3");
Thread.Sleep(3000);
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "0");
}
}
}
This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
WPF and cross thread operations
I am having trouble with a window in my wpf application displaying, but not updating the view. When placing the cursor over the opened window the loading icon is shown and the window is unresponsive. I am thinking this is likely due to some threading issue I don't have enough experience in seeing.
Here is the setup:
My main program runs on startup and creates and instance of a MainWindow window which implements a custom interface (IPlayer). The main program then runs a process which interracts with IPlayer to accomplish some task, the idea being that the main program requests actions from the MainWindow, which prompts the user for some sort of input and displays the results.
I'll simplify the code for clarity. Assume this program simply runs a sort of chatter bot game.
class MainProgram
{
[STAThread]
static void main(string[] args)
{
MainWindow wdw = new MainWindw();
Game g = new Game(wdw);
wdw.Show();
g.RunGame();
}
}
class Game
{
public IPlayer p;
Game(IPlayer) { this.p = p; }
public RunGame()
{
string r = GetResponse("How was your day?");
...
}
}
public partial class Human_Player : Window, IPlayer
{
public string GetResponse(string Question)
{
ShowQuestion(Question);
string r = GetResponse();
DisplayResponse(r);
return r;
}
...
}
I gave running RunGame() in a separate thread a shot like this:
Thread thread = new Thread(new ThreadStart(game.RunGame));
thread.Start();
but got an InvalidOperationException in response stating "The calling thread cannot access this object because a different thread owns it."
Any help here would be appreciated since I'm pretty new to this stuff. Thanks ahead of time!
EDIT:
Just to clarify, I'm not creating any new threads at the moment. Thus I don't think I'm doing any multi-threading. I'm attempting to run game.RunGame() on the main thread after opening the window. The runGame method consists of a large loop which calls a method on the Human_Player window that changes the UI.
As far as I know there are only two threads:
- Main Thread - MainProgram and Game run here. I think the windows runs here as well but I could be wrong... please clarify
- Rendering Thread - the UI is rendered here.
Argh, this question is asked soooo many times... You cannot update a GUI control from a different thread than the Dispatcher thread associated to the control. You will need to run your update code using Dispatcher.BeginInvoke to update the GUI from the correct thread.
If you are not running the code from a different thread then it's possible that your method is taking too long to execute, thus causing the GUI to hang because the event thread is blocked from accepting user input.
It sounds like your game code is running in a loop and stealing all your UI's processing time. It doesn't sound like you are releasing control to your dispatcher.
Your game code shouldn't loop, it should run an iteration once and then return control to the dispatcher. You need to create a timer control that will call your game code at regular intervals instead.
I'm trying to work with Threadding and it seems to me like it's suspiciously difficult (I'm probably doing it wrong).
I want to load a file inside a BackgroundWorker and while that happens, "send" each new line to a separate Thread (not bgWorker). I'm using BlockingCollection and Add() each line, then I want to Take() them and process them in another thread.
Now, everything is straightforward with the BgWorker; but why is it impossible(isn't it?) to just declare a new thread in Form1.cs and have it perform like the BgWorker? In other words, why must you create a separate WorkerClass ( http://msdn.microsoft.com/en-us/library/7a2f3ay4(VS.80).aspx )?
I'm asking this because, you can access your BlockingCollection fine from within the BackgroundWorker, but you can't do it from a separate WorkerClass (since it's a plain vanilla separate class). (So what's the point of the BlockingCollection then if you can't use it for what it's meant?)
Also, BgWorkers have a ReportProgress(...) event/method. As far as I know, if you use that msdn example, you don't have squat in your Thread.
What am I missing here? Please help.
PS: Before you jump and tell me that It's not in any way more efficient to send lines to another thread, know that I'm doing this as a learning exercise. Trying to figure out how Threads work in C# and how you sync/communicate between/with them (and/or bgWorkers).
Answering specifically why working with threads is more difficult than working with a background worker....
The backgroundworker is actually a method for creating another thread wrapped up in an easier to use package. The reason working with threads directly is harder is because it's closer to the real thing.
For a similar comparison, using System.Net.Mail to send an email is just a simplified way of creating socket connections, etc... Under the hood, the System.Net.Mail classes do the detailed work. Similarly, under the hood, the BackgroundWorker does the detailed work of dealing with the threads.
As a matter of fact, the MSDN documentaiton for the backgroundWorker object starts out like this:
BackgroundWorker Class Updated:
September 2010
Executes an operation on a separate
thread.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
So if the backgroundworker class is supposed to make threading easier, why would people want to work with threads directly? Because of the issue you're having. Sometimes the "friendly wrapper" leads to a loss of fine control.
Edit - added
What you're asking about in the comments is thread synchronization. This article covers it pretty well.
http://msdn.microsoft.com/en-us/magazine/cc164037.aspx
and this article answers "communicating between threads" explicitly.
http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic63233.aspx
To answer your question in the title, yes "normal" threads can act like BackgroundWorker threads. You just have to create more of the wiring code yourself.
I wrote a simple application for scanning my music collection using a manually created thread. The main body of the thread is a method that loops over all of the folders under a specified root and fires an event each time it encounters a folder that contains some mp3 files.
I subscribe to this event in the main form of my application and update a DataGridView with the new information.
So the thread is kicked off by the following code:
this.libraryThread = new Thread(new ThreadStart(this.library.Build)) { IsBackground = true };
// Disable all the buttons except for Stop which is enabled
this.EnableButtons(false);
// Find all the albums
this.libraryThread.Start();
The method supplied to ThreadStart does some housekeeping and then calls the method that does the work:
private void FindAlbums(string root)
{
// Find all the albums
string[] folders = Directory.GetDirectories(root);
foreach (string folder in folders)
{
if (this.Stop)
{
break;
}
string[] files = Directory.GetFiles(folder, "*.mp3");
if (files.Length > 0)
{
// Add to library - use first file as being representative of the whole album
var info = new AlbumInfo(files[0]);
this.musicLibrary.Add(info);
if (this.Library_AlbumAdded != null)
{
this.Library_AlbumAdded(this, new AlbumInfoEventArgs(info));
}
}
this.FindAlbums(folder);
}
}
When this method finishes a final LibraryFinished event is fired.
I subscribe to these events in the main form:
this.library.Library_AlbumAdded += this.Library_AlbumAdded;
this.library.Library_Finished += this.Library_Finished;
and in these methods add the new album to the grid:
private void Library_AlbumAdded(object sender, AlbumInfoEventArgs e)
{
this.dataGridView.InvokeIfRequired(() => this.AddToGrid(e.AlbumInfo));
}
and finish off (which reenables buttons etc.):
private void Library_Finished(object sender, EventArgs e)
{
this.dataGridView.InvokeIfRequired(() => this.FinalUpdate());
}
As you can see this is a lot of work which would be a whole lot simpler if I used a BackgroundWorker.