I have a class that populates 1000 ComboBoxes into the UI, the code is
namespace ActiveXMemory
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
PopulateControls();
}
public void PopulateControls() {
var newGrid = new Grid();
for (var i = 0; i < 1000; i++)
{
ComboBox newcombo = new ComboBox();
newGrid.Children.Add(newcombo);
}
this.Content = newGrid;
}
public bool OpenWindow(bool isRunning)
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
try
{
this.ShowDialog();
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
}));
return true;
}
public bool CloseWindow()
{
Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
{
try
{
this.Close();
}
catch (Exception exp)
{
//Console.WriteLine(exp.Message);
}
}));
return true;
}
private void Window_Closed(object sender, EventArgs e)
{
var grid = Content as Grid;
var children = grid.Children;
while (children.Count > 0)
{
var child = children[0];
grid.Children.Remove(child);
child = null;
}
grid = null;
}
}
}
I created an ActiveX library for the class to be accessible as ActiveX,
namespace ActiveXLibrary
{
[ComVisible(true)]
[Guid("EF2EAD91-68A8-420D-B5C9-E30A6F510BDE")]
public interface IActiveXLib
{
[DispId(1)]
bool Initialize();
[DispId(2)]
bool CloseActiveX();
}
[ComVisible(true)]
[Guid("9DACD44F-0237-4F44-BCB9-0E6B729915D6"),
ClassInterface(ClassInterfaceType.None)]
[ProgId("Samp")]
public class ActiveXLib : IActiveXLib
{
MainWindow form;
Thread thr;
public bool Initialize()
{
int i = 0;
try
{
ThreadStart exeFunc = new ThreadStart(() =>
{
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
{
start();
}));
});
thr = new Thread(exeFunc);
thr.SetApartmentState(ApartmentState.STA);
thr.Start();
while (form == null)
{
i++;
//Console.WriteLine("form Null");
System.Threading.Thread.Sleep(1000);
if (i > 30)
break;
}
return true;
}
catch (Exception exp)
{
Console.WriteLine("[Initialize]" + exp.Message);
return false;
}
}
public bool CloseActiveX()
{
bool success = false;
try
{
success = form.CloseWindow();
form = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
return success;
}
catch (Exception exp)
{
Console.WriteLine("[CloseActiveX]" + exp.Message);
return false;
}
}
private void start()
{
try
{
Console.WriteLine("start() - new MainWindow()");
form = new MainWindow();
Console.WriteLine("OpenWindow()");
form.OpenWindow(true);
}
catch (Exception exp)
{
MessageBox.Show(exp.Message + "\nPossible Reasons: " + exp.Source + "\n" + exp.InnerException, "Error");
}
}
}
}
Then I created a demo project as
ActiveXLib activeXRef;
public MainWindow()
{
InitializeComponent();
}
private void startButton_Click(object sender, RoutedEventArgs e)
{
activeXRef = new ActiveXLib();
activeXRef.Initialize();
}
private void stopButton_Click(object sender, RoutedEventArgs e)
{
activeXRef.CloseActiveX();
GC.Collect();
}
Here the problem is every time I click the start and stop button, the memory keeps on increasing, the memory (17MB) created by the first start of the application did not get released, even though I removed the ComboBoxes and set the grid content null. Even the GC.Collect() has no effect, since it only adds the garbage to a queue. Is there any fix for this.
I also tried flushing the memory as in the link, But even then the memory is still held.(since I am not sure whether the handle of the current process is from demo project/from ActiveXMemory)
Edit
I included the line
this.Dispatcher.Invoke(DispatcherPriority.Render, GCDelegate);
to the Window_Closed event
Now the memory gets cleared
But one problem I am facing now is, if I try to immediately close (call CloseActiveX()), then this is not happening.(i.e)
private void startButton_Click(object sender, RoutedEventArgs e)
{
activeXRef = new ActiveXLib();
activeXRef.Initialize();
activeXRef.CloseActiveX();
activeXRef = null;
GC.Collect();
}
The above code still has the memory locked, I don't understand the difference, it is just another event,Does anyone have any idea on this?
I think I got a repro for this problem although I can't tell what your MainWindow.Open/CloseWindow() methods look like. Using a thread is certainly part of the problem, there is one non-intuitive thing you have to do to prevent leaking internal WPF plumbing objects that have thread affinity.
It is imperative to shutdown the dispatcher for the thread. This normally just happens once in a WPF app when the UI thread terminates. Also very important that the dispatcher does the dispatching, WPF heavily depends on it to ensure that "weak events" are actually weak.
A sample implementation for MainWindow that does not leak:
public class MainWindow : Window {
public void OpenWindow(bool noidea) {
this.ShowDialog();
}
public bool CloseWindow() {
this.Dispatcher.Invoke(() => {
this.Dispatcher.InvokeShutdown(); // <== Important!
});
return true;
}
}
Where ShowDialog() ensures the dispatcher does the dispatching and the InvokeShutdown() ensures that the cleanup occurs. Tested by using a 10 msec DispatcherTimer instead of buttons so this happened at a very high rate. I could remove the GC.Collect() calls and memory usage was stable.
At the moment I've no comment about the ActiveX part (will double check and let you know), but I can suggest something related to the WPF code.
This should make the combo items disposable
public class DispCombo : ComboBox, IDisposable
{
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
public class DispGrid : Grid, IDisposable
{
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
You will populate disposable controls
public void PopulateControls()
{
var newGrid = new DispGrid();
for (var i = 0; i < 1000; i++)
{
DispCombo newcombo = new DispCombo();
newGrid.Children.Add(newcombo);
}
this.Content = newGrid;
}
So the closing phase would become (disposing instead of setting to null)
private void Window_Closed(object sender, EventArgs e)
{
var grid = Content as DispGrid;
var children = grid.Children;
while (children.Count > 0)
{
var child = children[0] as DispCombo;
grid.Children.Remove(child);
child.Dispose();
}
grid.Dispose();
}
That said, now you can wrap the WPF view in a using clause
public class MainLibrary
{
public bool OpenWindow() //found no ref to bool isRunning
{
using (var mw = new MainWindow())
{
mw.ShowDialog();
mw.Close();
}
return true;
}
public bool CloseWindow()
{
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
return true;
}
}
with your garbage collection in the close.
These are my memory snapshots after these changes, taken (1) at start, then (2) after the main ShowDialog in the OpenWindow and finally (3) after the CloseWindow
Edit
Finally, if you need to start a Task, calling the Dispatcher, here it is the function
public bool CloseWindow()
{
winClose = () =>
{
mw.Dispatcher.Invoke(() =>
{
mw.Close();
mw.Dispose();
});
};
cleanProc = () =>
{
mw.Dispatcher.Invoke(() =>
{
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
});
};
Task.Run(() =>
{
winClose.Invoke();
}).ContinueWith(x =>
{
cleanProc.Invoke();
});
return true;
}
with
public bool OpenWindow()
{
mw = new MainWindow();
mw.Show();
return true;
}
Related
I'm a fairly new developer and this one has me stumped.
My WinForms application is a slideshow for websites that rotates through a list of URLs, fading-in/out on each transition by using a second form as a "curtain". It's meant to run for an indefinite period of time but consistently hangs on the transition after running for a couple of days.
Form1:
HttpWebResponse response = null;
List<Slide.Doc> sList = null;
bool repeatSlideshow = true;
bool pageLoaded = false;
double curtainAnimStep = 0.05;
int errorCount = 0;
public Form1()
{
InitializeComponent();
CursorShown = false;
this.Visible = true;
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Maximized;
webBrowser1.ScrollBarsEnabled = false;
webBrowser1.ScriptErrorsSuppressed = true;
Slideshow(environment, channel);
}
public void Slideshow(string environment, string channel)
{
while (repeatSlideshow)
{
try
{
sList = Slide.convertJSONToSlide(Slide.getParams(environment, channel));
}
catch (Exception)
{
Form2 curtain = new Form2(curtainAnimStep);
curtain.Show();
waitForFade(curtain, 1);
displayError();
raiseCurtain(curtain, curtainAnimStep);
waitForFade(curtain, 0);
curtain.Dispose();
waitAround(30);
continue;
}
foreach (Slide.Doc s in sList)
{
bool slideWasDisplayed = false;
Form2 curtain = new Form2(curtainAnimStep);
curtain.Show();
waitForFade(curtain, 1);
slideWasDisplayed = displaySlide(s.URL_TEXT);
if (slideWasDisplayed == false)
{
webBrowser1.DocumentText = "<html><body style='background-color: #1C1C1C;'></body></html>";
redrawPage();
}
raiseCurtain(curtain, curtainAnimStep);
waitForFade(curtain, 0);
curtain.Dispose();
if (slideWasDisplayed == true)
{
waitAround(s.DISPLAY_SEC);
}
}
if (errorCount == sList.Count)
{
Form2 curtain = new Form2(curtainAnimStep);
curtain.Show();
waitForFade(curtain, 1);
displayError();
raiseCurtain(curtain, curtainAnimStep);
waitForFade(curtain, 0);
curtain.Dispose();
waitAround(30);
}
errorCount = 0;
Utilities.Web.WebBrowserHelper.WebBrowserHelper.ClearCache();
}
}
public bool displaySlide(string slideUrl)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(slideUrl);
request.Timeout = 1000;
try
{
response = (HttpWebResponse)request.GetResponse();
webBrowser1.Navigate(slideUrl);
redrawPage();
response.Dispose();
return true;
}
catch (WebException)
{
errorCount++;
return false;
}
}
public void redrawPage()
{
while (pageLoaded == false)
{
Application.DoEvents();
}
webBrowser1.Invalidate();
Application.DoEvents();
pageLoaded = false;
}
public void raiseCurtain(Form curtain, double curtainAnimStep)
{
while (curtain.Opacity > 0)
{
curtain.Opacity -= curtainAnimStep;
Application.DoEvents();
System.Threading.Thread.Sleep(10); // How long between shifts in opacity (NOT interval between slides)
}
}
public void waitAround(int duration)
{
DateTime dt2 = DateTime.Now;
while (dt2.AddSeconds(duration) > DateTime.Now)
{
Application.DoEvents();
}
}
public void waitForFade(Form curtain, int finalOpacity)
{
while (curtain.Opacity != finalOpacity)
{
DateTime dt = DateTime.Now;
dt = dt.AddSeconds(1);
while (dt > DateTime.Now)
{
Application.DoEvents();
}
}
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
pageLoaded = true;
}
Form2:
public Form2(double animStep)
{
InitializeComponent();
this.AnimStep = animStep;
}
public double AnimStep { get; set; }
private async void Form2_Load(object sender, EventArgs e)
{
while (Opacity < 1.0)
{
await Task.Delay(10);
Opacity += AnimStep;
}
Opacity = 1;
}
I've been working on this for a long time, but I have to admit that I genuinely don't even know what I should be looking for at this point.
Could the use of Application.DoEvents be responsible? Leaving them out breaks the application, but I can't figure out an alternative appproach.
Looking at your code (and as indicated by Noseratio) one of the things I advice is to get rid of the need for the DoEvents calls. Just remember that in Windows there is a dedicated UI thread that is used to update the controls on the form. As you are doing a lot of stuff (in loops, calling a bunch of methods) on that same UI thread the Windows controls depends on your cooperation to share some time with them, hence the calls to DoEvents.
I'm going to use a BackgroundWorker and a Timer and WaitHandle to schedule commands that will update the UI from a background thread. With that we do as little as needed on the UI thread.
Form Load
Form1 will only have a webbrowsercontrol and a backgroundworker. A queue will hold the commands that needs to be executed. From the Load event we start the Backgroundworker.
Form2 frm2 = new Form2();
Queue<ICommandExecutor> commands = new Queue<ICommandExecutor>();
private void Form1_Load(object sender, EventArgs e)
{
frm2.Show();
frm2.BringToFront();
commands.Enqueue(new LoadSlideShow(this, frm2, commands));
backgroundWorker1.RunWorkerAsync();
}
BackgroundWorker
The Backgroundworker DoWork event is the engine that runs on it's own background thread. It runs as long as there are commands found in the queue. After fetching a command it's Execute method is fired. If the command supports disposing the Dispose method is called and with that a command is processed and we start over again.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(commands.Count>0)
{
ICommandExecutor cmd = commands.Dequeue();
try
{
cmd.Execute();
// dispose if we can
IDisposable sync = cmd as IDisposable;
if (sync != null)
{
sync.Dispose();
}
}
catch(Exception exp)
{
// add commands here
Trace.WriteLine("error" + exp.Message);
}
}
}
Commands
There is a standard interface available to implement a command pattern. ICommandExecutor has a single method, Execute. We can create different classes that implement this interface. Each class holds its own state and references and it can be as simple as a timer of as complex as loading a new batch of urls to show.
public class ShowSlide:ICommandExecutor
{
string url;
Form1 form;
AutoResetEvent done = new AutoResetEvent(false);
public ShowSlide(Form1 form, string url)
{
this.url = url;
this.form = form;
}
public void Execute()
{
// if we are not on the UI thread...
if (form.InvokeRequired)
{
// ... switch to it...
form.Invoke(new MethodInvoker(Execute));
}
else
{
// .. we are on the UI thread now
// reused from your code
form.displaySlide(url);
}
}
}
Here is a timer. Notice how a Timer class is used and the timerDone waithandle to make the backgroundthread continue work only if the timer has finished when Dispose is called.
public class WaitForSeconds: ICommandExecutor, IDisposable
{
int ms;
System.Threading.Timer timer;
ManualResetEvent timerDone = new ManualResetEvent(false);
public WaitForSeconds(int secs)
{
this.ms = secs * 1000;
}
public void Execute()
{
// use a timer
timer = new System.Threading.Timer(
(state) => timerDone.Set() // signal we are done
);
timerDone.Reset();
timer.Change(this.ms, Timeout.Infinite);
}
public void Dispose()
{
timerDone.WaitOne();
timerDone.Dispose();
timer.Dispose();
}
}
To setup the commands in the correct order we use the following command class implememntation that takes the Command queue, Form1 and Form2 as parameters on its constructor. The Execute command loads all url's to be fed to the webbrowser control. For each url it adds the commands that needs to be executed to the queue. At the end the this instance is added to the queue as well which means the class will be used again if all commands have been processed. The queue will there for never be empty.
public class LoadSlideShow: ICommandExecutor
{
readonly Queue<ICommandExecutor> commands;
readonly Form1 form;
readonly Form2 form2;
public LoadSlideShow(Form1 form, Form2 form2, Queue<ICommandExecutor> cmds)
{
this.form = form;
commands = cmds;
this.form2 = form2;
}
public void Execute()
{
var list = Slide.convertJSONToSlide(null);
foreach (var slide in list)
{
commands.Enqueue(new ShowSlide(form, slide.URL_TEXT));
commands.Enqueue(new WaitForSeconds(1));
//commands.Enqueue(new LowerCurtain(form2));
commands.Enqueue(new WaitForSeconds(slide.DISPLAY_SEC));
//commands.Enqueue(new RaiseCurtain(form2));
}
commands.Enqueue(this);
}
}
This is basically all there is that is needed to get a basic slideshow going.
For the so called curtain we are going to do something similar with Form2 but I'll use the BackgroundWorker_progress event as well.
Form2 the Curtain
Form2 will act as the curtain by changing it's Opacity in a loop. It has it's own backgroundworker:
ManualResetEvent stateChange = new ManualResetEvent(false);
public ManualResetEvent stateChangeDone = new ManualResetEvent(false);
private void Form2_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(stateChange.WaitOne())
{
stateChange.Reset();
var progressDone = new AutoResetEvent(false);
int progress = 0;
using(var timer = new System.Threading.Timer(_=>
{
backgroundWorker1.ReportProgress(progress);
progress += 2;
if (progress>=100)
{
progressDone.Set();
}
}, null, 0, 25))
{
progressDone.WaitOne();
}
stateChangeDone.Set();
}
}
The background worker calls ResportProgress with an int indicating its prpgress. That causes the ProgressChanged event to be raised. Based on what state the Curtain needs to be in, we calculate the correct value for the Opacity.
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
switch(state)
{
case Curtain.Up:
this.Opacity = e.ProgressPercentage / 100.0;
break;
case Curtain.Down:
this.Opacity = (100 - e.ProgressPercentage) / 100.0;
break;
}
}
To get this all started we create two public methods called Up and Down:
enum Curtain
{
Up,
Down
}
Curtain state;
public void Up()
{
state = Curtain.Up;
stateChange.Set();
stateChangeDone.Reset();
}
public void Down()
{
state = Curtain.Down;
stateChange.Set();
stateChangeDone.Reset();
}
With that we are only left with the implementation of the Command classes that will be added to the Command queue and handled by the background worker of Form1:
public class RaiseCurtain:ICommandExecutor, IDisposable
{
readonly Form2 form2;
public RaiseCurtain( Form2 form2)
{
this.form2 = form2;
}
public void Execute()
{
if (form2.InvokeRequired)
{
form2.Invoke(new MethodInvoker(Execute));
}
else
{
form2.BringToFront();
form2.Up();
}
}
public void Dispose()
{
form2.stateChangeDone.WaitOne();
}
}
public class LowerCurtain : ICommandExecutor,IDisposable
{
readonly Form2 form2;
public LowerCurtain(Form2 form2)
{
this.form2 = form2;
}
public void Execute()
{
if (form2.InvokeRequired)
{
form2.Invoke(new MethodInvoker(Execute));
}
else
{
form2.Down();
}
}
public void Dispose()
{
form2.stateChangeDone.WaitOne();
}
}
That is it. We have eliminated the use of DoEvents.
There is one caveat: this doesn't guarantee that the application will stop again after a couple of hours/days. The reason for this is a possible memory-leak in the webbrowser control and in my testing I did see the same effect, a slowly but steadily increasing private memory consumption while the managed memory bytes stayed virtually the same.
As none of the posts provided a definitive answer one option could be to restart your app as indicates in one of the answers here. On the plus side, you can implement this now as a Command class...
The problem that I am working on deals with out to use a functional Lock, or monitor structure, to provided exclusive access to only one member on separate threads. Below, is my class definition of the monitor (note that it is different than the actual monitor class given by c# library). What I am trying to do is make pictureboxes appear or disappear on my form.
I have attempted to add in an instance of the form so I can access the individual pictureboxes, however, my program seems to freeze.
namespace SmokersProblem
{
class monitor
{
Form1 form = new Form1();
Random num = new Random();
object obj = new object();
public monitor()
{
}
public void agent(){
form.pictureBox4.Visible = false;
int r = num.Next(1, 4);
if (r == 1)
{
// put lighter and paper on table
smoker1();
}
else if (r == 2)
{
// put lighter and tobacco on table
smoker2();
}
else
{
// put paper and tobacco on table
smoker3();
}
}
public void smoker1()
{
//lock (obj)
//{
form.pictureBox9.Visible = true;
form.pictureBox1.Visible = false;
System.Threading.Thread.Sleep(5000);
//agent();
// }
}
public void smoker2()
{
//lock (obj)
//{
form.pictureBox10.Visible = true;
form.pictureBox3.Visible = false;
System.Threading.Thread.Sleep(5000);
//agent();
//}
}
public void smoker3()
{
//lock (obj)
//{
form.pictureBox11.Visible = true;
form.pictureBox2.Visible = false;
System.Threading.Thread.Sleep(5000);
//agent();
// }
}
}
}
Below is my form code, as you can see here, i try to create three seperate threads, one for each smoker.
namespace SmokersProblem
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Random rnd = new Random();
int num = rnd.Next(1, 4);
Object obj = new Object();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
pictureBox2.Visible = true;
pictureBox3.Visible = true;
pictureBox8.Visible = false;
pictureBox7.Visible = false;
pictureBox6.Visible = false;
monitor one = new monitor();
one.agent();
Thread vone = new Thread(one.smoker1);
Thread two = new Thread(one.smoker2);
Thread three = new Thread(one.smoker3);
vone.Start();
two.Start();
three.Start();
}
}
}
After implementing this, I went looking for the Smoker Thread Problem that it looks like OP is trying to implement. This code should be easily adaptable to that problem.
The reason your UI is freezing is that you're calling one.agent() without putting it in a new thread. one.agent() sleeps, which keeps your UI from processing events.
OK, I've implemented some code to do the smoker problem with labels. Obviously it could be improved, for instance by not coupling the form to the threads.
I put two different locking mechanisms in, and left one commented out.
Essentially, there are three labels that can either be "Smoking" or "Not Smoking". The main UI thread creates three threads:
Smoker1
Smoker2
Smoker3
Each of the threads continually tries to take the lock in a while loop. When they take the lock, they set their label to "Smoking", wait a few seconds, and then set their label to "Not Smoking". This uses thread safe code from this answer.
public partial class Form1 : Form
{
private bool running = false;
public Label OneLabel { get; set; }
public Label TwoLabel { get; set; }
public Label ThreeLabel { get; set; }
private MyMonitor one;
private Thread vone;
private Thread two;
private Thread three;
public Form1()
{
InitializeComponent();
OneLabel = new Label();
OneLabel.Text = "Not Smoking";
OneLabel.Location = new Point(10, 50);
OneLabel.AutoSize = true;
this.Controls.Add(OneLabel);
TwoLabel = new Label();
TwoLabel.Text = "Not Smoking";
TwoLabel.Location = new Point(150, 50);
this.Controls.Add(TwoLabel);
ThreeLabel = new Label();
ThreeLabel.Text = "Not Smoking";
ThreeLabel.Location = new Point(300, 50);
this.Controls.Add(ThreeLabel);
}
private void MainButton_Click(object sender, EventArgs e)
{
if (!running)
{
vone.Start();
two.Start();
three.Start();
MainButton.Text = "Stop";
running = true;
}
else
{
one.RequestStop();
MainButton.Text = "Run";
running = false;
}
}
private void Form1_Load(object sender, EventArgs e)
{
one = new MyMonitor(this);
vone = new Thread(one.Smoker1);
two = new Thread(one.Smoker2);
three = new Thread(one.Smoker3);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (running)
{
one.RequestStop();
running = false;
}
}
}
class MyMonitor
{
private int x = 1;
private Object obj = new Object();
private Form1 _form;
bool _finished = false;
public MyMonitor(Form1 form)
{
_form = form;
}
public void Smoker1()
{
while (!_finished)
{
//lock (obj)
//{
// _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Smoking");
// System.Threading.Thread.Sleep(2000);
// _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Not Smoking");
//}
try
{
Monitor.Enter(obj);
try
{
_form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Smoking");
System.Threading.Thread.Sleep(2000);
_form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Not Smoking");
}
finally
{
Monitor.Exit(obj);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine(SyncEx.Message);
}
}
}
public void Smoker2()
{
while (!_finished)
{
//lock (obj)
//{
// _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Smoking");
// System.Threading.Thread.Sleep(2000);
// _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Not Smoking");
//}
try
{
Monitor.Enter(obj);
try
{
_form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Smoking");
System.Threading.Thread.Sleep(2000);
_form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Not Smoking");
}
finally
{
Monitor.Exit(obj);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine(SyncEx.Message);
}
}
}
public void Smoker3()
{
while (!_finished)
{
//lock (obj)
//{
// _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Smoking");
// System.Threading.Thread.Sleep(2000);
// _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Not Smoking");
//}
try
{
Monitor.Enter(obj);
try
{
_form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Smoking");
System.Threading.Thread.Sleep(2000);
_form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Not Smoking");
}
finally
{
Monitor.Exit(obj);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine(SyncEx.Message);
}
}
}
public void RequestStop()
{
_finished = true;
}
}
//Thread Safe Extension Method
public static class Extensions
{
private delegate void SetPropertyThreadSafeDelegate<TResult>(Control #this, Expression<Func<TResult>> property, TResult value);
public static void SetPropertyThreadSafe<TResult>(this Control #this, Expression<Func<TResult>> property, TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;
if (propertyInfo == null ||
!#this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
#this.GetType().GetProperty(propertyInfo.Name, propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}
if (#this.InvokeRequired)
{
#this.Invoke(new SetPropertyThreadSafeDelegate<TResult>(SetPropertyThreadSafe), new object[] { #this, property, value });
}
else
{
#this.GetType().InvokeMember(propertyInfo.Name, BindingFlags.SetProperty, null, #this, new object[] { value });
}
}
}
my program seems to freeze
My one.agent() is the part of the code that allows one of the smokers
to be called on, so they can smoke. Why wouldnt I want it in the main
code?
Because you shouldn't be using Sleep() from the main UI thread, which is what happens when you call one.agent() from the Button Click event. When Sleep(5000) is hit, you're telling the forms main UI thread to not do ANYTHING for five seconds, thus the freezing you're seeing.
To fix this, you'd need agent() to execute smoker1(), smoker2(), or smoker3() in a separate thread like you're doing down below.
There are several other problems with the code, however, that must also be addressed before you can "fix" your code...
The next problem lies in you creating a new instance of Form1() inside your monitor() class. This instance of Form1() is not the same one that is visible on your screen. Acting upon it is modifying an invisible form that has never even been shown. To act upon the form that is actually visible on your screen would require you to either (a) pass a reference to it into your monitor() class when you create it, or (b) have your monitor() class raise custom events that Form1() subscribes to, again when it creates monitor().
The last problem lies in you attempting to change UI controls from within a thread other than the main UI thread. This will result in a cross-thread exception (unless you've turn this off, which you shouldn't). There are various ways to overcome this problem, the most basic of which involves using delegates and the Invoke() method of the Form/Control to which you are trying to update.
I want to know when all my async threads have completed so I know when to close my loading form. My code never closes the loading form. I don't know why. I'm unsure how to correctly pass my ManualResetEvent object to the async thread too.
I'm also open to a simpler means to achieve my goal of knowing when to close the loading form.
UPDATE
After reading the advice here I've updated my class. Unfortunetly, it still does not work. I feel closer though. It's just that the callback never fires.
using System;
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.Threading.Tasks;
using System.Threading;
namespace BrianTests
{
public class TaskInfo
{
public RegisteredWaitHandle Handle;
public string OtherInfo = "default";
public Form loading;
}
public partial class AsyncControlCreateTest : Form
{
//List<ManualResetEvent> MREs = new List<ManualResetEvent>();
Form loading = new Form() { Text = "Loading...", Width = 100, Height = 100 };
CountdownWaitHandle cdwh;
public AsyncControlCreateTest()
{
InitializeComponent();
}
private void AsyncControlCreateTest_Load(object sender, EventArgs e)
{
loading.Show(this);//I want to close when all the async threads have completed
CreateControls();
}
private void CreateControls()
{
int startPoint= 0;
int threadCount = 2;
cdwh = new CountdownWaitHandle(threadCount);
for (int i = 0; i < threadCount; i++)
{
ManualResetEvent mre = new ManualResetEvent(initialState: true);
UserControl control = new UserControl() { Text = i.ToString() };
control.Load += new EventHandler(control_Load);
Controls.Add(control);
control.Top = startPoint;
startPoint += control.Height;
//MREs.Add(mre);
//mre.Set();//just set here for testing
}
Task.Factory.StartNew(new Action(() =>
{
TaskInfo info = new TaskInfo();
info.loading = loading;
try
{
info.Handle = ThreadPool.RegisterWaitForSingleObject(cdwh, WaitProc, info, 4000, executeOnlyOnce: false);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}));
}
public static void WaitProc(object state, bool timedOut)
{//this callback never occurs...
TaskInfo ti = (TaskInfo)state;
string cause = "TIMED OUT";
if (!timedOut)
{
cause = "SIGNALED";
// If the callback method executes because the WaitHandle is
// signaled, stop future execution of the callback method
// by unregistering the WaitHandle.
if (ti.Handle != null)
ti.Handle.Unregister(null);
}
Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
ti.OtherInfo,
Thread.CurrentThread.GetHashCode().ToString(),
cause
);
ti.loading.Close();
}
void control_Load(object sender, EventArgs e)
{
RichTextBox newRichTextBox = new RichTextBox();
UserControl control = sender as UserControl;
control.Controls.Add(newRichTextBox);
Task.Factory.StartNew(new Action(() =>
{
Thread.Sleep(2000);
newRichTextBox.Invoke(new Action(() => newRichTextBox.Text = "loaded"));
cdwh.Signal();
}));
}
}
public class CountdownWaitHandle : WaitHandle
{
private int m_Count = 0;
private ManualResetEvent m_Event = new ManualResetEvent(false);
public CountdownWaitHandle(int initialCount)
{
m_Count = initialCount;
}
public void AddCount()
{
Interlocked.Increment(ref m_Count);
}
public void Signal()
{
if (Interlocked.Decrement(ref m_Count) == 0)
{
m_Event.Set();
}
}
public override bool WaitOne()
{
return m_Event.WaitOne();
}
}
}
The problem is that WaitHandle.WaitAll is throwing an exception, which you can see:
try
{
WaitHandle.WaitAll(MREs.ToArray());
}
catch (Exception e) {
MessageBox.Show(e.Message);
throw;
}
The error message is that "WaitAll for multiple handles on a STA thread is not supported."
If you do something like
foreach(var m in MREs)
m.WaitOne();
It will work.
I'm not quite sure why the exception did not crash the application, as I would have hoped. See perhaps How can I get WinForms to stop silently ignoring unhandled exceptions? for this.
Locking the MRE's and moving the WaitAll off the STA thread does the trick.
public partial class AsyncControlCreateTest : Form
{
object locker = new object();
static List<ManualResetEvent> MREs = new List<ManualResetEvent>();
Form loading = new Form() { Text = "Loading...", Width = 100, Height = 100 };
public AsyncControlCreateTest()
{
InitializeComponent();
}
private void AsyncControlCreateTest_Load(object sender, EventArgs e)
{
loading.Show(this);//I want to close when all the async threads have completed
CreateControls();
}
private void CreateControls()
{
int startPoint= 0;
for (int i = 0; i < 100; i++)
{
ManualResetEvent mre = new ManualResetEvent(initialState: false);
UserControl control = new UserControl() { Text = i.ToString() };
control.Load += new EventHandler(control_Load);
Controls.Add(control);
control.Top = startPoint;
startPoint += control.Height;
MREs.Add(mre);
}
Task.Factory.StartNew(new Action(() =>
{
try
{
WaitHandle.WaitAll(MREs.ToArray());
}
catch (Exception ex)
{
MessageBox.Show("error " + ex.Message);
}
finally
{
MessageBox.Show("MRE count = " + MREs.Count);//0 count provides confidence things are working...
loading.Invoke(new Action( () => loading.Close()));
}
}));
}
void control_Load(object sender, EventArgs e)
{
RichTextBox newRichTextBox = new RichTextBox();
UserControl control = sender as UserControl;
control.Controls.Add(newRichTextBox);
Task.Factory.StartNew(new Action(() =>
{
Thread.Sleep(500);
newRichTextBox.Invoke(new Action(() => newRichTextBox.Text = "loaded"));
lock (locker)
{
var ev = MREs.First();
MREs.Remove(ev);
ev.Set();
}
}));
}
}
I have a BindingList databound to a datgridview. I'm using it to keep track of some real-time prices. The method 'update(Quote quote)' is called multiple times a second by various threads. If the datagridview doesn't contain the Quote, it is added. If it does, the values of the quote are updated. I don't want the same quote to appear in the BindingList (or on the GUI) twice, so I tried to put a lock around the operation that checks whether the value is in the list or not. It doesn't work! What am I doing wrong? I've tried two different ways of locking, and am locking on a String object rather than just an object. The problem is definitely in the BeginInvoke(new MethodInvoker(delegate() { activeQuotes.Insert(0, quote); })); call (which is probably taking some time), but if I make that synchronous, the 'add' method throws a 'cross-threading' error. . . What can I do to avoid the cross-threading error, but ensure that the lock works also??
public BindingList<Quote> activeQuotes = new BindingList<Quote>();
object lockObject = "lockObject";
dataGridViewActive.DataSource = activeQuotes;
public void update(Quote quote)
{
//lock (lockObject)
if(Monitor.TryEnter(lockObject))
{
try
{
if (!activeQuotes.Contains(quote))
{
try
{
activeQuotes.Add(quote);
AddQuote(quote);
}
catch (Exception ex)
{
Console.WriteLine("Datagridview!!!!!!");
}
}
else
{
int index = activeQuotes.IndexOf(quote);
activeQuotes[index].Bid = quote.Bid;
activeQuotes[index].Ask = quote.Ask;
activeQuotes[index].Mid = quote.Mid;
activeQuotes[index].Spread = quote.Spread;
activeQuotes[index].Timestamp = quote.Timestamp;
}
finally
{
Monitor.Exit(lockObject);
}
}
private void AddQuote(Quote quote)
{
if (this.InvokeRequired)
{
BeginInvoke(new MethodInvoker(delegate() { activeQuotes.Insert(0, quote); }));
BeginInvoke(new MethodInvoker(delegate() { dataGridViewActive.Refresh(); }));
BeginInvoke(new MethodInvoker(delegate() { dataGridViewActive.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells); }));
}
else
{
activeQuotes.Add(quote);
dataGridViewActive.Refresh();
dataGridViewActive.AutoResizeColumns (DataGridViewAutoSizeColumnsMode.AllCells);
}
}
I'd appreciate any help at all on this.
Thanks.
this code I wrote before will do
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
BindingListInvoked<Name> names;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
names = new BindingListInvoked<Name>(dataGridView1);
dataGridView1.DataSource = names;
new Thread(() => names.Add(new Name() { FirstName = "Larry", LastName = "Lan" })).Start();
new Thread(() => names.Add(new Name() { FirstName = "Jessie", LastName = "Feng" })).Start();
}
}
public class BindingListInvoked<T> : BindingList<T>
{
public BindingListInvoked() { }
private ISynchronizeInvoke _invoke;
public BindingListInvoked(ISynchronizeInvoke invoke) { _invoke = invoke; }
public BindingListInvoked(IList<T> items) { this.DataSource = items; }
delegate void ListChangedDelegate(ListChangedEventArgs e);
protected override void OnListChanged(ListChangedEventArgs e)
{
if ((_invoke != null) && (_invoke.InvokeRequired))
{
IAsyncResult ar = _invoke.BeginInvoke(new ListChangedDelegate(base.OnListChanged), new object[] { e });
}
else
{
base.OnListChanged(e);
}
}
public IList<T> DataSource
{
get
{
return this;
}
set
{
if (value != null)
{
this.ClearItems();
RaiseListChangedEvents = false;
foreach (T item in value)
{
this.Add(item);
}
RaiseListChangedEvents = true;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
}
}
public class Name
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
I think you should change your BeginInvoke to just Invoke. You need to get it on the UI thread, not begin an async operation. Otherwise your lock could get released before the BeginInvoke target gets invoked because control is returned immediately upon calling BeginInvoke. Calling Invoke will block that thread on that call until the Invoke target completes then return control back to your thread, which will ensure the lock is kept.
Also, have you considered using a lock block instead of Monitor method calls? It's basically the same thing but prevents you from needing the try/finally. I don't see that you're using any retry or benefit from the TryEnter, but perhaps the code sample doesn't demonstrate that.
This is my first post here, but I've using this site regularly to help me with my own app's, and I should say that this site has been a great help to me, so thanks to everyone.
Now my question:
I'm developing my first software app that exchanges data between a sql server and the app itself. It's beeing developed in C#. Saving or retreiving data from the sql server database is no problem.
What I want is a way to inform the user of the delay between the local machine (where the app is installed) and the server. I can make some animations or simply display some text messages. What I need help with is how to create the code that activates/fires/runs when that server communication time is running.
If you can't understand the idea, picture a video game. When it's loading (in some games) you can see the loading screen before the game starts. I need some code that displays that "loading window" when the the app is downloading or uploading data from/to the server.
I would appreciate any code example or web site recommendation.
PS: Sorry for the extensive text, but I want to make sure everyone understand so I don't have to repeat it again :P
How do I implement a progress bar in C#?
How to create a smooth progress bar in Visual C#
ProgressBar Class
I have developed a simple PleaseWait class 2 years ago, but I didn't update this class, It works very well, have look hope this will give you an idea to implement your logic.
public partial class frmWait : Form
{
public frmWait()
{
InitializeComponent();
}
bool _isMoving = false;
int _moveStart_x = 0;
int _moveStart_y = 0;
private void tmrProgress_Tick(object sender, EventArgs e)
{
if (barProgress.Value == barProgress.Maximum)
barProgress.Value = barProgress.Minimum;
else
barProgress.Value += 1;
}
private void btnCancel_Click(object sender, EventArgs e)
{
Close();
PleaseWait.Abort();
}
protected override CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams p = base.CreateParams;
p.ClassStyle += 0x20000;
p.ExStyle += 0x8000000;
return p;
}
}
protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 132;
base.WndProc(ref m);
switch (m.Msg)
{
case WM_NCHITTEST:
if (m.Result.ToInt32() == 1)
m.Result = new IntPtr(2);
break;
}
}
private void panelEx1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_isMoving = true;
_moveStart_x = e.X;
_moveStart_y = e.Y;
}
}
private void panelEx1_MouseUp(object sender, MouseEventArgs e)
{
_isMoving = false;
}
private void pnlContainer_MouseMove(object sender, MouseEventArgs e)
{
if (_isMoving)
this.Location = new Point(Location.X + e.X - _moveStart_x, Location.Y + e.Y - _moveStart_y);
}
}
public class PleaseWait
{
#region Static Operations
private static Boolean _isAborted = false;
private static Boolean _isVisible = false;
private static frmWait _waitForm;
private static String _waitingState = "";
private static Boolean _autoClose = false;
private static Boolean _cancelable = false;
private static System.Threading.Thread _waiterThred;
public delegate void CancelButtonPressed();
public static event CancelButtonPressed OnCancel;
public static Boolean AutoClose
{
get { return PleaseWait._autoClose; }
set { PleaseWait._autoClose = value; }
}
public static string WaitingState
{
get { return PleaseWait._waitingState; }
set { PleaseWait._waitingState = value; }
}
public static bool IsVisible
{
get { return _isVisible; }
internal set { _isVisible = value; }
}
public static void ShowPleaseWait()
{
ShowPleaseWait("", _autoClose, false);
}
public static void ShowPleaseWait(string waitingState)
{
ShowPleaseWait(waitingState, _autoClose, false);
}
public static void ShowPleaseWait(bool autoClose)
{
ShowPleaseWait("", autoClose, false);
}
public static void ShowPleaseWait(string waitingState, bool autoClose, bool cancelable)
{
if (_waiterThred != null)
{
if (_isVisible)
{
// the please wait it woking, just continue and apply the changes
_waitingState = waitingState;
_autoClose = autoClose;
_cancelable = cancelable;
return;
}
else
{
_waiterThred.Abort();
_waiterThred = null;
}
}
_waitingState = waitingState;
_autoClose = autoClose;
_cancelable = cancelable;
_isAborted = false;
_isVisible = false;
if (_autoClose)
Application.Idle += new EventHandler(Application_Idle);
_waiterThred = new System.Threading.Thread(DisplayWaitingForm);
_waiterThred.IsBackground = true;
_waiterThred.Name = "Please Wait....";
_waiterThred.Start();
Application.DoEvents();
}
public static void Abort()
{
_isAborted = true;
}
private static void Application_Idle(object sender, EventArgs e)
{
if (_autoClose)
_isAborted = true;
}
private static void DisplayWaitingForm()
{
if (_waitForm != null)
{
if (!_waitForm.IsDisposed)
_waitForm.Dispose();
_waitForm = null;
_isVisible = false;
}
try
{
if (_isAborted)
return;
_waitForm = new frmWait();
if (_cancelable)
{
_waitForm.btnCancel.Enabled = true;
_waitForm.btnCancel.Click += new EventHandler(btnCancel_Click);
}
try
{
_isVisible = true;
_waitForm.Show();
_waitForm.Focus();
while (!_isAborted)
{
System.Threading.Thread.Sleep(15);
_waitForm.lblMessage.Text = _waitingState;
Application.DoEvents();
_waitForm.lblMessage.Text = _waitingState;
}
_isVisible = false;
}
finally
{
FreeWaitingForm();
}
}
finally
{
_isVisible = false;
}
}
static void btnCancel_Click(object sender, EventArgs e)
{
if (_waitForm.InvokeRequired)
{
_waitForm.BeginInvoke(new EventHandler(btnCancel_Click), new object[] { e });
}
else
{
if (OnCancel != null)
OnCancel.Invoke();
}
}
private static void FreeWaitingForm()
{
_waitingState = "";
_isVisible = false;
if (_waitForm == null)
{
return;
}
_waitForm.Hide();
if (!_waitForm.IsDisposed)
_waitForm.Dispose();
_waitForm = null;
}
#endregion
}
use like following code :
PleaseWait.ShowPleaseWait("Please wait", true, false);
// If second param is true then it will close the form automatically.
// If third param is true the it will expose a cancel button, so you can cancel your Asynchronous operations.
I didn't insert design code, you can understand by looking at code.
hope this help.
First let me thank you for your replies.
Toby your answer got me thinking about thread monitoring my sql connections but it was a bit tricky and confusing since the app is still in develop and will use a lot more connections.
S.Amani answer it wasn't quite what I want, but thanks to that I found a easier way. I created a form (could be anything else), placed a label saying: Saving To Data Base, took out the top bar, defined location and defined it's parent to be disabled when shown and enabled when closed. The following code is what I put inside my DataBaseInteractionClass
private Wait myCustomWaitDialog = new Wait(); // My Waiting form
private void SaveToDatabase(myObjectToSave obj) // Method called to save data do DB
{
// Create the connections and queries
(...)
// This is what I did
// Show Waiting Form
myCustomWaitDialog.Show();
// Instanciate the command that will carry the query and to DB
SqlCommand command = new SqlCommand(Queries.GetData(code), conn);
// This is important
//Create event that will fire when the command completes
command.StatementCompleted += new StatementCompletedEventHandler(command_StatementCompleted);
// Execute the transaction
SqlDataReader reader = command.ExecuteReader();
// Rest of the code (validations, close connections, try/catch, etc
(...)
}
void command_StatementCompleted(object sender, StatementCompletedEventArgs e)
{
// This is the method that closes my Waiting Dialog
myCustomWaitDialog.CloseDialog();
myCustomWaitDialog.Dispose();
}
It's not quite what I want yet, but is the best solution that I found so far. For now it will do :)
Anyway, thanks for the replies and I hope this helps someone else.