getting stackoverflow exception while passing value in different thread in c# - c#

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

Related

occasionally problems with invokerequired in c#

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.

Add finished speaking callback to text-to-speech function

I'm using this code that works perfectly except I want to add the ability to know when a startSpeaking call is done speaking.
static class VoiceEffect
{
SpeechSynthesizer reader = new SpeechSynthesizer();
private volatile bool _isCurrentlySpeaking = false;
/// <summary>Event handler. Fired when the SpeechSynthesizer object starts speaking asynchronously.</summary>
private void StartedSpeaking(object sender, SpeakStartedEventArgs e)
{ _isCurrentlySpeaking = true; }
/// <summary>Event handler. Fired when the SpeechSynthesizer object finishes speaking asynchronously.</summary>
private void FinishedSpeaking(object sender, SpeakCompletedEventArgs e)
{ _isCurrentlySpeaking = false; }
private VoiceEffect _instance;
/// <summary>Gets the singleton instance of the VoiceEffect class.</summary>
/// <returns>A unique shared instance of the VoiceEffect class.</returns>
public VoiceEffect GetInstance()
{
if(_instance == null)
{ _instance = new VoiceEffect(); }
return _instance;
}
/// <summary>
/// Constructor. Initializes the class assigning event handlers for the
/// SpeechSynthesizer object.
/// </summary>
private VoiceEffect()
{
reader.SpeakStarted += new EventHandler<SpeakStartedEventArgs>(StartedSpeaking);
reader.SpeakCompleted += new EventHandler<SpeakCompletedEventArgs>(FinishedSpeaking);
}
/// <summary>Speaks stuff.</summary>
/// <param name="str">The stuff to speak.</param>
public void startSpeaking(string str)
{
reader.Rate = -2; // Voice effects.
reader.Volume = 100;
// if the reader's currently speaking anything,
// don't let any incoming prompts overlap
while(_isCurrentlySpeaking)
{ continue; }
reader.SpeakAsync(str);
}
/// <summary>Creates a new thread to speak stuff into.</summary>
/// <param name="str">The stuff to read.</param>
public void createVoiceThread(string str)
{
Thread voicethread = new Thread(() => startSpeaking(str)); // Lambda Process
voicethread.IsBackground = true;
voicethread.Start();
}
}
from this question https://stackoverflow.com/a/17153718/1137006
I call that by writing this in another class:
TextToSpeech.startSpeaking(text);
I want to know when that call is done and it has finished speaking.
Possibly as an event?
I can see the event inside the VoiceEffect class but I don't know how to get it to fire in the class that do the startSpeaking() call.
The reason I need to know is to change a WPF-control after something has been spoken.
Is that possible to add to this code?
Edit: To clarify
In my MainWindow class I can call multiple TextToSpeech from the file TextToSpeech.cs that has the VoiceEffect class and startSpeaking method.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TextToSpeech.startSpeaking("Test1");
TextToSpeech.startSpeaking("Test2");
TextToSpeech.startSpeaking("Test3");
TextToSpeech.startSpeaking("Test4");
}
}
They will all wait for the one previous to be done before speaking and does also let the program continue forward during the time it's speaking.
I want to know if I can get an event or something when for example "Test2" has been spoken and then change a WPF-control in the MainWindow? For example hiding a Text label.
Is this what you are looking for?
public MainWindow() {
InitializeComponent();
SpeechSynthesizer reader = new SpeechSynthesizer();
reader.SpeakCompleted += Reader_SpeakCompleted;
}
void Reader_SpeakCompleted(object sender, SpeakCompletedEventArgs e) {
}

Cross-thread cross-form. Display a splash screen with a progress bar

My Solution:
So I managed to find another tutorial http://www.codeproject.com/KB/dotnet/Yet_Another_Splash_Screen.aspx and the sourcecode seemed to make more sense to me. Here is the code i'm using now. Main() is left untouched.
Splash.cs
`
public partial class Frm_Splash : Form
{
delegate void ProgressDelegate(int percent);
delegate void SplashShowCloseDelegate();
/// <summary>
/// To ensure splash screen is closed using the API and not by keyboard or any other things
/// </summary>
bool CloseSplashScreenFlag = false;
/// <summary>
/// Base constructor
/// </summary>
///
public Frm_Splash()
{
InitializeComponent();
progress_Splash.Show();
this.ClientSize = this.BackgroundImage.Size;
}
public void ShowSplashScreen()
{
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new SplashShowCloseDelegate(ShowSplashScreen));
return;
}
this.Show();
Application.Run(this);
}
/// <summary>
/// Closes the SplashScreen
/// </summary>
public void CloseSplashScreen()
{
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new SplashShowCloseDelegate(CloseSplashScreen));
return;
}
CloseSplashScreenFlag = true;
this.Close();
}
/// <summary>
/// Update text in default green color of success message
/// </summary>
/// <param name="Text">Message</param>
public void Progress(int percent)
{
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new ProgressDelegate(Progress), new object[] { percent });
return;
}
// Must be on the UI thread if we've got this far
progress_Splash.Value = percent;
// Fade in the splash screen - looks pro. :D
if (percent < 10)
this.Opacity = this.Opacity + .15;
}
/// <summary>
/// Prevents the closing of form other than by calling the CloseSplashScreen function
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SplashForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (CloseSplashScreenFlag == false)
e.Cancel = true;
}
}`
Form1.cs
public partial class Frm_Main : Form
{
Frm_Splash frm_Splash = new Frm_Splash();
public Frm_Main()
{
this.Hide();
Thread splashthread = new Thread(new ThreadStart(frm_Splash.ShowSplashScreen));
splashthread.IsBackground = true;
splashthread.Start();
InitializeComponent();
CenterToScreen();
}
private void Frm_Main_Load(object sender, EventArgs e)
{
if (PassedAll() == true)
FillMovieLB();
if (FillMovieProgress == 100)
{
//Throw in this sleep so the user can see the progress bar reach all the way to the end.
Thread.Sleep(1000);
this.Show();
frm_Splash.CloseSplashScreen();
this.Activate();
}
}
Original Question
G'day all,
I'm very new to programming in C# and i'm having a problem with the http://www.codeproject.com/KB/cs/prettygoodsplashscreen.aspx tutorial and implementing it within my application. I'm finding it a little difficult to understand what the problem is. I know there is alot of stuff about getting this splash screen to work but I can't get my head around it.
When I start the program, the Frm_Main will display, you can see the listbox being populated, because i've placed it in BackgroundWorker.DoWork(), and then afterwards my frm_Splash will show after the work is done. Obviously, the way it should be working is, frm_Splash will show during the work being done on Frm_Main, and the progress bar will show the progress of the loading (this part I haven't implemented yet).
Edit: I may not have been clear, but the question is: How can I get my splashscreen to display while the work is being done and before the main form is displayed?
Thanks everybody. :)
Here is my code:
static Frm_Splash frm_Splash = new Frm_Splash();
public delegate void ShowFormDelegate();
public void ShowForm()
{
frm_Splash.Show();
}
public Frm_Main()
{
InitializeComponent();
CenterToScreen();
if (PassedAll() == true)
{
back_loadprog.RunWorkerAsync();
}
}
private void back_loadprog_DoWork(object sender, DoWorkEventArgs e)
{
Invoke(new ShowFormDelegate(ShowForm));
Invoke(new FillMovieLBDelegate(FillMovieLB));
}
Here, have some code... Works for me.
Splash Form:
namespace Screens.Forms
{
public partial class Splash : DevExpress.XtraEditors.XtraForm
{
public Splash()
{
InitializeComponent();
}
string RandomLoadingMessage()
{
string[] lines ={
"Pripremam warp pogon",
"Moj drugi ekran za učitavanje je brži, probaj njega",
"Verzija programa koju imam u testiranju imala je smiješnije poruke"
};
return lines[new Random().Next(lines.Length)];
}
public void RandomizeText()
{
lblMessage.Text = RandomLoadingMessage();
}
private void Splash_Load(object sender, EventArgs e)
{
RandomizeText();
}
private static Splash _splash;
private static bool _shouldClose;
static void ThreadFunc()
{
_splash = new Splash();
_splash.Show();
while (!_shouldClose)
{
Application.DoEvents();
Thread.Sleep(100);
if (new Random().Next(1000) < 10)
{
_splash.Invoke(new MethodInvoker(_splash.RandomizeText));
}
}
for (int n = 0; n < 18; n++)
{
Application.DoEvents();
Thread.Sleep(60);
}
if (_splash != null)
{
_splash.Close();
_splash = null;
}
}
static public void ShowSplash()
{
_shouldClose = false;
Thread t = new Thread(ThreadFunc);
t.Priority = ThreadPriority.Lowest;
t.Start();
}
internal static void RemoveSplash()
{
_shouldClose = true;
}
internal static void ShowSplash(List<string> fromTwitterMessages)
{
ShowSplash();
}
}
}
Show it with:
Splash.ShowSplash();
Do the work you need, then when done:
Splash.RemoveSplash();
You need to take this a step further back to your Main() function of the application.
In general you could do this:
Create a ManualResetEvent or better ManualResetEventSlim if you are on .NET 4
Start a new thread displaying your SplashScreen, use Application.Run
In your SplashScreen you should create a time which polls the created ManualResetEvent
frequently, a nice animation could be placed here also
If the event is set you should close the form
Back in your Main() do your stuff like creating forms etc.
When finish set the event, so that the SplashScreen can be closed
To be sure that your MainForm is not shown before your SplashScreen is closed you can use another event

Fire event from Async component in UI thread

I'm building a non-visual component in .Net 2.0. This component uses an asynchronous socket (BeginReceive, EndReceive etc). Asynchronous callbacks are called in the context of a worker thread created by the runtime. The component user shouldn't have to worry about multithreading (This is the main goal, what I want)
The component user can create my non-visual component in any thread (the UI thread is just a common thread for simple applications. More serious applications could create the component within an arbitrary worker thread). The component trigger events such as "SessionConnected" or "DataAvailable".
The issue: because of the Async Callbacks and the events raised therein the event handler is executed in the worker thread context. I want to use an intermediate layer which force
the event handler to execute in the context of the thread which created the
component at the first place.
Example code (stripped from exception handling etc...)
/// <summary>
/// Occurs when the connection is ended
/// </summary>
/// <param name="ar">The IAsyncResult to read the information from</param>
private void EndConnect(IAsyncResult ar)
{
// pass connection status with event
this.Socket.EndConnect(ar);
this.Stream = new NetworkStream(this.Socket);
// -- FIRE CONNECTED EVENT HERE --
// Setup Receive Callback
this.Receive();
}
/// <summary>
/// Occurs when data receive is done; when 0 bytes were received we can assume the connection was closed so we should disconnect
/// </summary>
/// <param name="ar">The IAsyncResult that was used by BeginRead</param>
private void EndReceive(IAsyncResult ar)
{
int nBytes;
nBytes = this.Stream.EndRead(ar);
if (nBytes > 0)
{
// -- FIRE RECEIVED DATA EVENT HERE --
// Setup next Receive Callback
if (this.Connected)
this.Receive();
}
else
{
this.Disconnect();
}
}
Because of the nature of the Async sockets all applications using my component are littered with "If (this.InvokeRequired) { ..." and all I want is the user to be able to use my component worry-free as sort of a drop-in.
So how would I go about raising the events without requiring the user to check InvokeRequired (or, put differently, how do I force the events raised in the same thread as the thread that initiated the event in the first place)?
I have read stuff about AsyncOperation, BackgroundWorkers, SynchronizingObjects, AsyncCallbacks and tons of other stuff but it all makes my head spin.
I did come up with this, surely clumsy, "solution" but it seems to fail in some situations (when my component is called from a WinForms project via a static class for example)
/// <summary>
/// Raises an event, ensuring BeginInvoke is called for controls that require invoke
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
/// <remarks>http://www.eggheadcafe.com/articles/20060727.asp</remarks>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
try
{
Control ed = eventDelegate.Target as Control;
if ((ed != null) && (ed.InvokeRequired))
ed.Invoke(eventDelegate, args);
else
eventDelegate.DynamicInvoke(args);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType());
Console.WriteLine(ex.Message);
//Swallow
}
}
}
Any help would be appreciated. Thanks in advance!
EDIT:
According to this thread my best bet would be to use SyncrhonizationContext.Post but I can't see how to apply it to my situation.
Ok; so here's what I ended up with after some more reading:
public class MyComponent {
private AsyncOperation _asyncOperation;
/// Constructor of my component:
MyComponent() {
_asyncOperation = AsyncOperationManager.CreateOperation(null);
}
/// <summary>
/// Raises an event, ensuring the correct context
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
_asyncOperation.Post(new System.Threading.SendOrPostCallback(
delegate(object argobj)
{
eventDelegate.DynamicInvoke(argobj as object[]);
}), args);
}
}
}
The other solution posted here was sort of a work-in-progress. The solution posted here seems (according to MSDN) be the best so far. Suggestions are very, very welcome.
I seem to have found my solution:
private SynchronizationContext _currentcontext
/// Constructor of my component:
MyComponent() {
_currentcontext = WindowsFormsSynchronizationContext.Current;
//...or...?
_currentcontext = SynchronizationContext.Current;
}
/// <summary>
/// Raises an event, ensuring the correct context
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
if (_currentcontext != null)
_currentcontext.Post(new System.Threading.SendOrPostCallback(
delegate(object a)
{
eventDelegate.DynamicInvoke(a as object[]);
}), args);
else
eventDelegate.DynamicInvoke(args);
}
}
I'm still testing this but it seems to work fine.
Maybe I'm not understanding the issue, but it seems to me that you can just pass a reference to a custom object in your Async state.
I put together the following example to illustrate;
First we have a Callback object. This has 2 properties -- a Control on which to dispatch actions and an Action to call;
public class Callback
{
public Control Control { get; set; }
public Action Method { get; set; }
}
Then I have a WinForms project that calls some random code on another thread (using BeginInvoke) and then shows a messagebox when the code finishes executing.
private void Form1_Load(object sender, EventArgs e)
{
Action<bool> act = (bool myBool) =>
{
Thread.Sleep(5000);
};
act.BeginInvoke(true, new AsyncCallback((IAsyncResult result) =>
{
Callback c = result.AsyncState as Callback;
c.Control.Invoke(c.Method);
}), new Callback()
{
Control = this,
Method = () => { ShowMessageBox(); }
});
}
The ShowMessageBox method must run on the UI thread and looks like:
private void ShowMessageBox()
{
MessageBox.Show("Testing");
}
Is this what you were looking for?
If your component must always be used by the same thread, you could do something like this:
public delegate void CallbackInvoker(Delegate method, params object[] args);
public YourComponent(CallbackInvoker invoker)
{
m_invoker = invoker;
}
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
try
{
if (m_invoker != null)
m_invoker(eventDelegate, args);
else
eventDelegate.DynamicInvoke(args);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType());
Console.WriteLine(ex.Message);
//Swallow
}
}
}
Then when you instantiate your component from a Form or other control you can do this:
YourComponent c = new YourComponent(this.Invoke);
To queue the event on a non UI worker thread, it must have some sort of work queuing mechanism, then you can give a method with CallbackInvoker's signature to queue the delegate on the worker thread.

C# Multi Thread Design Example

I am relatively new to C#/.Net. I'm developing a desktop application that requires multi threading. I came up with the following pattern below as a base. I was wondering if anyone could point out how to make it better in terms of coding, being thread safe, and being efficient.
Hopefully this makes some sense.
public abstract class ThreadManagerBase
{
// static class variables
private static ThreadManagerBase instance = null;
private static BackgroundWorker thread = null;
private static ProgressBarUIForm progress = null;
/// <summary>
/// Create a new instance of this class. The internals are left to the derived class to figure out.
/// Only one instance of this can run at any time. There should only be the main thread and this thread.
/// </summary>
public abstract static ThreadManagerBase NewInstance();
/// <summary>
/// Clears the instance.
/// </summary>
public static void ClearInstance()
{
instance = null;
}
/// <summary>
/// Initializes the background worker with some presets.
/// Displays progress bar.
/// </summary>
private abstract static void InitializeThread()
{
thread = new BackgroundWorker();
thread.WorkerReportsProgress = true;
thread.WorkerSupportsCancellation = true;
thread.DoWork += new DoWorkEventHandler(thread_DoWork);
thread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(thread_RunWorkerCompleted);
thread.ProgressChanged += new ProgressChangedEventHandler(thread_ProgressChanged);
thread.RunWorkerAsync();
progress = new ProgressBarUIForm();
progress.EnableCancelButton = true;
progress.UserCanceled += new EventHandlerCancelClicked(progress_UserCanceled);
progress.ShowDialog();
thread.Dispose();
thread = null;
}
private static void progress_UserCanceled(bool userCanceled)
{
thread.CancelAsync();
}
private static void thread_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progress.SetProgressLevel = e.ProgressPercentage;
progress.SetProgressMessage = e.UserState.ToString();
}
private static void thread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progress.Close();
progress = null;
}
private static void thread_DoWork(object sender, DoWorkEventArgs e)
{
ProcessWork();
}
private abstract static void ProcessWork()
{
// do actuall stuff here.
// the derived classes will take care of the plumbing.
}
}
Have you looked into the Microsoft Parallel Extensions to .NET Framework 3.5? It's a pretty good library that takes a lot of the work out of threading.
There are also a lot of articles on MSDN about threading patterns that you should research too. Threading can get really complicated really fast. It's nice to have someone else to have though of all the important stuff that can go wrong, and simplify it down to a library or a pattern. Of course, there's danger in that too if you don't understand the gotchas of any particular solution. So, make sure you research well whatever solution you choose.
I don't see a good reason to create this abstraction over BackgroundWorker.
If you insist, just a warning: I'm not sure if it changed in later releases, but in NET 2.0, it wasn't possible to really cancel the DoWork handler (unless it checked once in a while if it was asked to stop). Read here for a solution.
I have done something similar to this. There is a good reason if you do have multiple tasks that you want to perform, but you dont want to have BackgroundWorker code replicated through the entire project. I dont have the progressbar tied to the actual base class, I just have that in the main form. Here is the solution I came up with:
The following is the base class:
public abstract class Operation
{
#region public Event Handlers
///
/// The event that updates the progress of the operation
///
public event OperationProgressChangedEventHandler OperationProgressChanged;
///
/// The event that notifies that the operation is complete (and results)
///
public event OperationCompletedEventHandler OperationCompleted;
#endregion
#region Members
// Whether or not we can cancel the operation
private bool mWorkerSupportsCancellation = false;
// The task worker that handles running the operation
private BackgroundWorker mOperationWorker;
// The operation parameters
private object[] mOperationParameters;
#endregion
///
/// Base class for all operations
///
public Operation(params object[] workerParameters)
{
mOperationParameters = workerParameters;
// Setup the worker
SetupOperationWorker();
}
#region Setup Functions
///
/// Setup the background worker to run our Operations
///
private void SetupOperationWorker()
{
mOperationWorker = new BackgroundWorker();
mOperationWorker.WorkerSupportsCancellation = mWorkerSupportsCancellation;
mOperationWorker.WorkerReportsProgress = true;
mOperationWorker.WorkerSupportsCancellation = true;
mOperationWorker.DoWork += new DoWorkEventHandler(OperationWorkerDoWork);
mOperationWorker.ProgressChanged += new ProgressChangedEventHandler(OperationWorkerProgressChanged);
mOperationWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OperationWorkerRunWorkerCompleted);
}
#endregion
#region Properties
///
/// Whether or not to allow the user to cancel the operation
///
public bool CanCancel
{
set
{
mWorkerSupportsCancellation = value;
}
}
#endregion
#region Operation Start/Stop Details
///
/// Start the operation with the given parameters
///
/// The parameters for the worker
public void StartOperation()
{
// Run the worker
mOperationWorker.RunWorkerAsync(mOperationParameters);
}
///
/// Stop the operation
///
public void StopOperation()
{
// Signal the cancel first, then call cancel to stop the test
if (IsRunning())
{
// Sets the backgroundworker CancelPending to true, so we can break
// in the sub classes operation
mOperationWorker.CancelAsync();
// This allows us to trigger an event or "Set" if WaitOne'ing
Cancel();
// Wait for it to actually stop before returning
while (IsRunning())
{
Application.DoEvents();
}
}
}
///
/// Whether or not the operation is currently running
///
///
public bool IsRunning()
{
return mOperationWorker.IsBusy;
}
#endregion
#region BackgroundWorker Events
///
/// Fires when the operation has completed
///
///
///
private void OperationWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Allow the sub class to clean up anything that might need to be updated
Clean();
// Notify whoever is register that the operation is complete
if (OperationCompleted != null)
{
OperationCompleted(e);
}
}
///
/// Fires when the progress needs to be updated for a given test (we might not care)
///
///
///
private void OperationWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Notify whoever is register of what the current percentage is
if (OperationProgressChanged != null)
{
OperationProgressChanged(e);
}
}
///
/// Fires when we start the operation (this does the work)
///
///
///
private void OperationWorkerDoWork(object sender, DoWorkEventArgs e)
{
// Run the operation
Run(sender, e);
}
#endregion
#region Abstract methods
///
/// Abstract, implemented in the sub class to do the work
///
///
///
protected abstract void Run(object sender, DoWorkEventArgs e);
///
/// Called at the end of the test to clean up anything (ex: Disconnected events, etc)
///
protected abstract void Clean();
///
/// If we are waiting on something in the operation, this will allow us to
/// stop waiting (ex: WaitOne).
///
protected abstract void Cancel();
#endregion
}
The following is an example test class for the example I posted:
class TestOperation : Operation
{
AutoResetEvent mMsgRec;
public TestOperation(params object[] workerParameters)
: base(workerParameters)
{
CanCancel = true;
mMsgRec = new AutoResetEvent(false);
//mSomeEvent += DoSomething();
}
protected override void Cancel()
{
mMsgRec.Set();
}
protected override void Clean()
{
//mSomeEvent -= DoSomething();
}
protected override void Run(object sender, DoWorkEventArgs e)
{
BackgroundWorker bg = (BackgroundWorker)sender;
for (int i = 0; !bg.CancellationPending && (i &lt 90); i++)
{
bg.ReportProgress(i);
Thread.Sleep(100);
}
for (int i = 90; !bg.CancellationPending && (i &lt 100); i++)
{
mMsgRec.WaitOne(2000, false);
bg.ReportProgress(i);
}
if (bg.CancellationPending)
{
e.Cancel = true;
}
else
{
e.Result = "Complete"; // Or desired result
}
}
}
And here is what the main form would look like (very basic example):
public partial class Form1 : Form
{
TestOperation t;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
t = new TestOperation();
t.CanCancel = true;
t.OperationProgressChanged += new OperationProgressChangedEventHandler(t_OperationProgressChanged);
t.OperationCompleted += new OperationCompletedEventHandler(t_OperationCompleted);
t.StartOperation();
}
void t_OperationCompleted(RunWorkerCompletedEventArgs e)
{
progressBar1.Value = 0;
}
void t_OperationProgressChanged(ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void button2_Click(object sender, EventArgs e)
{
t.StopOperation();
}
}
I'm currently investigation Threadmare over at http://sklobovsky.nstemp.com/community/threadmare/threadmare.htm for a C# project. It looks very, very useful. It's in Delphi, but the principles apply to any language that can handle events.
You don't need a BackgroundWorker unless you want to be spoonfed, normal threads are perfectly acceptable, as long as you follow the rules.

Categories

Resources