I have created a simple test-program with two Forms. One containing a button to start the procedure and on containing only an image (or in another testcase a byte[]). Both contain a timer to allow the rendering of the forms.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
timer1.Interval = 0;
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
// Instantiate and show Form2
timer1.Enabled = false;
var form2 = new Form2();
form2.ShowDialog();
// Dispose, force Collect and show (estimated) memory usage
form2.Dispose();
GC.Collect();
System.Diagnostics.Debug.WriteLine(GC.GetTotalMemory(true));
timer1.Enabled = true;
}
}
The other Form only closes itself (there is an PictureBox in the .designer-Part):
public partial class Form2 : Form
{
// Alternative test
// private byte[] bytes;
public Form2()
{
InitializeComponent();
timer1.Interval = 0;
timer1.Enabled = true;
//bytes = new byte[1024*1024];
}
private void timer1_Tick(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Ignore;
}
}
The Point is: it doesn't matter what exactly is in Form2: the memory usage rises and at one point I'll get an OutOfMemoryException (or in case of the PictureBox a System.Exception ending in
at Microsoft.AGL.Common.MISC.HandleAr(PAL_ERROR ar)
at System.Drawing.Bitmap._InitFromMemoryStream(MemoryStream mstream)
but that seems to be an OutOfMemoryException in a native call which is not thrown properly.)
I don't understand why. There is no reference left to the old instance of Form2, I call Dispose and yet the memory is not released.
I use CF 3.5 for this.
Ok, multiple things were at work here:
The event handler for the Tick-event held an implicit reference to the form.
The timer1_Tick needs a reference to the form. As long as there is a reference to the form in the timer and a reference in the form to the pointer, the GC (at least the CF one, I did't test this on the regular Framework) cannot free the memory.
The components where not disposed.
It seems it isn't enough to call Dispose on the form. I also had to call Dispose on the Timer to free all of its memory. This is a bug.
public partial class Form2 : Form
{
private byte[] bytes;
public Form2()
{
InitializeComponent();
timer1.Interval = 0;
timer1.Enabled = true;
bytes = new byte[1024 * 1024];
}
private void timer1_Tick(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Ignore;
this.timer1.Tick -= timer1_Tick;
this.timer1.Dispose();
this.mainMenu1 = null;
}
}
Related
I am new to JavaFx/Concurrency so I read the tutorial over at Concurrency in JavaFX but I am still a little confused about the implementation of background threads in a JavaFX Gui.
I'm trying to write a small GUI that interfaces with some serial devices (using JSSC-2.8) and that updates the GUI based on the responses from those devices. But, there's a lag between when the message is written and when the device responds, and using Thread.sleep() for an arbitrary amount of time wasn't a reliable way for me program it. So instead I want to use wait() and notify() methods from the concurrency package (with all the appropriate synchronizations), but I am not sure how to implement it. What I initially did is create another Thread, inside the Task, that would write the messages and wait for the responses, and using some bindings, would update the GUI. I've included my code at the end. Here is a short form of the pseudocode I am trying to implement:
start Task:
connect to serial devices
synchronized loop:
send messages
wait() for event to fire
notify()
But what's been happening is, as soon as I call the wait(), the entire application idles and then when notify() is called (after the response fires and event), it doesn't continue where it left off in the recipe() loop, or the startTdk() loop for that matter, it's just idle. Have I implements the threads wrong? When I am calling the wait(), is it a possibility that I cause the EventDispatch or JavaFX Application Thread to pause?
I hope the question is clear, if there are any clarifications needed I can update the post.
public class OmicronRecipe extends Service<String> implements Runnable{
private final String SEPERATOR=";";
private final Tdk tdk;
private final Pvci pvci;
private final SimpleStringProperty data = new SimpleStringProperty("");
private final Float MAX_V = 26.0f,UHV=1e-8f;
private boolean isTdkOn=false, isPvciOn=false;
private String power;
private Float temp,press,maxT, setT;
private int diffMaxT,diffP,diffPow, diffT, index=0;
public OmicronRecipe(){
tdk = new Tdk("COM4");
pvci = new Pvci("COM5");
}
private synchronized void recipe(){
while (true){
try {
sendMessages();
data.set(power+SEPERATOR+temp+SEPERATOR+press);
calcDiffs();
if (diffPow < 0){
if(diffMaxT < 0){
if(diffT < 0){
if (diffP < 0){
if(!rampPow()){
//Max Power reached
}
}else{
//Wait for pressure drop
}
}
}else{
//Wait until quit
}
}else{
//Max power reached
}
Thread.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private synchronized boolean rampPow(){
boolean isRamped=false;
Float setPow = tdk.getSetPow(index), curPow;
setT = tdk.getSetT(index);
curPow = Float.parseFloat(power);
if(curPow.compareTo(setPow) < 0){
do{
curPow += 0.1f;
tdk.sendMessage("PV "+curPow+"\r");
try {
wait();
} catch (InterruptedException ex) {
Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
}
curPow = Float.parseFloat(power);
}while(curPow.compareTo(setPow) < 0);
index++;
isRamped=true;
}
return isRamped;
}
public synchronized boolean connect(){
if(!isTdkOn && !isPvciOn){
isTdkOn = tdk.connect();
isPvciOn = pvci.connect();
}
return isTdkOn && isPvciOn;
}
public synchronized boolean disconnect(){
if(tdk!=null && pvci !=null){
isTdkOn = tdk.disconnect();
isPvciOn = pvci.disconnect();
}
return !isTdkOn && !isPvciOn;
}
public synchronized StringProperty getData(){
return data;
}
public void setMaxT(Float maxT){
this.maxT = maxT;
}
private synchronized void calcDiffs(){
Float pow = Float.parseFloat(power);
diffPow = pow.compareTo(MAX_V);
diffMaxT = temp.compareTo(maxT);
diffT = temp.compareTo(100f);
diffP = press.compareTo(UHV);
}
private synchronized void setListeners(){
tdk.getLine().addListener((ov,t, t1)-> {
synchronized (this){
System.out.println("New Power: "+t1);
power = t1;
this.notify();
}
});
pvci.getLine().addListener((ov,t,t1) ->{
synchronized (this){
String[] msg = t1.split(SEPERATOR);
if(msg.length == 2){
switch(msg[0]){
case "temperature":
System.out.println("Temperaute");
temp = Float.parseFloat(msg[1]);
break;
case "pressure":
System.out.println("Pressure");
press = Float.parseFloat(msg[1]);
break;
default:
System.out.println("Nothing; Something went wrong");
break;
}
}
this.notify();
}
});
}
private synchronized void sendMessages(){
try {
tdk.sendMessage("PV?\r");
this.wait();
pvci.sendMessage("temperature");
this.wait();
pvci.sendMessage("pressure");
this.wait();
} catch (InterruptedException ex) {
Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
}
}
private synchronized boolean startTdk(){
boolean isOut=false;
if(isTdkOn){
try {
tdk.sendMessage("ADR 06\r");
this.wait();
System.out.println("Power: "+power);
if(power.equals("OK")){
tdk.sendMessage("OUT?\r");
this.wait();
if(power.equals("OFF")){
tdk.sendMessage("OUT ON\r");
this.wait();
isOut = power.equals("ON");
}
else{
isOut = power.equals("ON");
}
}
} catch (InterruptedException ex) {
Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
}
}
return isOut;
}
#Override
protected Task<String> createTask() {
return new Task<String>() {
#Override
protected String call() throws IOException{
new Thread(new OmicronRecipe()).start();
return "";
}
};
}
#Override
public void run() {
if (connect()){
setListeners();
if(startTdk()){
recipe();
}
}
}
}
I won't include the Pvci class, because it just a copy of the Tdk class but with specific message sequences to talk with that machine.
public class Tdk {
private SerialPort tdkPort;
private final String portName;
private StringBuilder sb = new StringBuilder("");;
private final StringProperty line = new SimpleStringProperty("");
private final HashMap<Float,Float> calibMap;
private ArrayList<Float> list ;
private boolean isEnd=false;
public Tdk(String portName){
this.portName = portName;
System.out.println("TDK at "+portName);
calibMap = new HashMap();
setMap();
}
public synchronized boolean connect(){
tdkPort = new SerialPort(portName);
try {
System.out.println("Connecting");
tdkPort.openPort();
tdkPort.setParams(9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
tdkPort.setEventsMask(SerialPort.MASK_RXCHAR);
tdkPort.addEventListener(event -> {
if(event.isRXCHAR()){
if(event.getPortName().equals(portName)){
try {
if(!isEnd){
int[] str = tdkPort.readIntArray();
if(str!=null)
hexToString(str);
}
if(isEnd){
System.out.println("Here: "+sb.toString());
isEnd=false;
String d = sb.toString();
sb = new StringBuilder("");
line.setValue(d);
}
} catch (SerialPortException e) {
Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
}
}
}
});
} catch (SerialPortException e) {
Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
}
return tdkPort !=null && tdkPort.isOpened();
}
public synchronized boolean disconnect(){
if(tdkPort!=null) {
try {
tdkPort.removeEventListener();
if (tdkPort.isOpened())
tdkPort.closePort();
} catch (SerialPortException e) {
Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
}
System.out.println("Disconnecting");
}
return tdkPort.isOpened();
}
public synchronized void sendMessage(String message){
try {
tdkPort.writeBytes(message.getBytes());
} catch (SerialPortException e) {
Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
}
}
private void setMap(){
calibMap.put(1.0f, 25.0f);
calibMap.put(7.0f, 125.0f);
calibMap.put(9.8f, 220.0f);
list = new ArrayList(calibMap.keySet());
}
public Float getSetPow(int index){
return list.get(index);
}
public Float getSetT(int index){
return calibMap.get(list.get(index));
}
public synchronized StringProperty getLine(){
return line;
}
private synchronized void hexToString(int[] hexVal){
for(int i : hexVal){
if(i != 13){
sb.append((char)i);
}else{
isEnd=true;
}
}
System.out.println("Turning: "+Arrays.toString(hexVal)+" to String: "+sb.toString()+" End: "+isEnd);
}
Freeze
Your UI freezes most probably because you are waiting on the FX Apllication Thread, to solve this there are different approaches:
JavaFX Application Thread
You can delegate some work to the FX Application Thread, therefore see Platform.runLater
Not everything can be run on this thread, but for example, in your DeviceController, you can wait until the message appears and then call Platform.runLater() and update the field (you should therefor oc hand the field over to the controller).
DataBinding
What you are describing can also be realised with DataBinding.
With this you could define a SimpleStringProperty, which is bound to your UI Label (.bind() Method). If the controller must fire its message you can set the StringProperty and the UI will update itself.
The scenario you described could be used like this:
start Task:
connect to serial devices
synchronized loop:
send messages
wait() for event to fire
**updateDate the DataBounded fields**
We are taught that, Concurrency notify/wait
Concurrency on level wait()/notify() is very low level. You should try to work with higher level synchronisation methods or helpers (where people have already solved your problems :))
This is Form1
public static BackgroundWorker[] threadArray;
//public static AutoResetEvent[] _eventHandles ;
public static readonly object _lockObj = new object();
public static bool _go ;
//public static ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
private void BackgroundWorkerFilesDoWork(object sender, DoWorkEventArgs e)
{
...
lock (_lockObj)
{
//_manualResetEvent.WaitOne(30000);
//_manualResetEvent.Reset();
//if (clsValueStatic.CaptchaText != null)
//{
// foreach (var id in clsValueStatic.CaptchaText)
// {
while (!_go)
{
Monitor.Wait(_lockObj);
}
// }
//}
}
...
}
private void btn_Start_Scraping_Click(object sender, EventArgs e)
{
ServicePointManager.DefaultConnectionLimit = slider_Thread.Value;
threadArray = new BackgroundWorker[listView_Site.Items.Count];
for (var f = 0; f < listView_Site.Items.Count; f++)
{
threadArray[f] = new BackgroundWorker();
threadArray[f].DoWork += new DoWorkEventHandler(BackgroundWorkerFilesDoWork);
threadArray[f].RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorkerFilesRunWorkerCompleted);
threadArray[f].ProgressChanged += new ProgressChangedEventHandler(BackgroundWorkerFilesProgressChanged);
threadArray[f].WorkerReportsProgress = true;
threadArray[f].WorkerSupportsCancellation = true;
threadArray[f].RunWorkerAsync(listView_Site.Items[f].Tag.ToString());
}
}
This is Form2
private void textBoxCaptcha_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
var textBox = sender as TextBoxX;
if (textBox != null)
{
lock (frmScrapingAnalysis._lockObj)
{
//clsValueStatic.CaptchaText = null;
////frmScrapingAnalysis._go = true;
//clsValueStatic.CaptchaText.Add(textBox.Tag.ToString(), textBox.Text.Trim());
//textBox.Parent.Parent.Dispose();
Monitor.Pulse(frmScrapingAnalysis._lockObj);
}
//frmScrapingAnalysis._manualResetEvent.Set();
//frmScrapingAnalysis._eventHandles[frmScrapingAnalysis.positionThread].Set();
}
}
}
this is program to login website
Form1 have 1 button to start multiple backgroundworker and show form2 then all backgroundworker wait to get text captcha of textbox from form2
my way want when user enter text of backgroundworker is shown on form2 then only the backgroundworker is released. All other backgroundworker still wait
I try to code and debug with AutoResetEvent/ManualResetEvent and Monitor.Wait()/Pulse() and EventWaitHandle but order execution of all backgroundworker is FIFO
3 days ago, I still have not been anyway to solve this problem :(
Or other anyway can solve my problem. Thank you so much
Every time when my apps went to the layout that download image online, the device will hang and need to wait the download finish only can movable.
I did some researched. They recommend download it in another Thread. However, I not understand how to implement the download function in another Thread.
Here is my code to call the download image function.
Main.getUiApplication().invokeLater(new Runnable() {
public void run() {
for (j = 0; j < imagepath.length; j++) {
if (!imagepath[j].toString().equals("no picture")
&& Config_GlobalFunction.isConnected()) {
loader = new Util_LazyLoader(imagepath[j],
new Util_BitmapDowloadListener() {
public void ImageDownloadCompleted(
Bitmap bmp) {
imagebitmap[j] = bmp;
invalidate();
}
});
loader.run();
}
}
}
}, 500, false);
And the lazyloader
public class Util_LazyLoader implements Runnable {
String url = null;
Util_BitmapDowloadListener listener = null;
public Util_LazyLoader(String url, Util_BitmapDowloadListener listener) {
this.url = url;
this.listener = listener;
}
public void run() {
Bitmap bmpImage = getImageFromWeb(url);
listener.ImageDownloadCompleted(bmpImage);
}
private Bitmap getImageFromWeb(String url) {
HttpConnection connection = null;
InputStream inputStream = null;
EncodedImage bitmap;
byte[] dataArray = null;
try {
connection = (HttpConnection) (new ConnectionFactory())
.getConnection(url + Database_Webservice.ht_params)
.getConnection();
int responseCode = connection.getResponseCode();
if (responseCode == HttpConnection.HTTP_OK) {
inputStream = connection.openDataInputStream();
dataArray = IOUtilities.streamToBytes(inputStream);
}
} catch (Exception ex) {
} finally {
try {
inputStream.close();
connection.close();
} catch (Exception e) {
}
}
if (dataArray != null) {
bitmap = EncodedImage.createEncodedImage(dataArray, 0,
dataArray.length);
return bitmap.getBitmap();
} else {
return null;
}
}
}
I need help on it as I not familiar in networking.
So, the Util_LazyLoader is already well written to support background image downloads, because it implements the Runnable interface. You can start the download like this:
Util_LazyLoader loader =
new Util_LazyLoader(imagepath[j],
new Util_BitmapDowloadListener() {
public void ImageDownloadCompleted(final Bitmap bmp) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
imagebitmap[j] = bmp;
invalidate();
}
});
}
});
Thread backgroundWorker = new Thread(loader);
backgroundWorker.start();
instead of directly calling the loader.run() method yourself.
A Runnable class is just one that has a run() method. You give your Runnable loader object to a new Thread and tell it to start(). This will cause that Thread to execute the run() method in another thread, instead of the UI thread. As long as you don't run network operations on the UI thread, your app should not appear to the user to be frozen.
Note: in your original code, you have this:
Main.getUiApplication().invokeLater(new Runnable() {
public void run() {
You probably don't need that at all. If that code is being run from the main (UI) thread, then all that's doing is telling the app to invoke that locally-defined run() method, also on the UI thread. You do pass a 500 millisecond delay as well. Maybe you need that (?). If you just want it to run right away, though, get rid of the code above (invokeLater(new Runnable() { public void run() { ...). Just use the code I posted (at the top of this answer) to create the backgroundWorker and then call its start() method.
Also, take note of two things in my implementation:
1. I used the UiApplication.invokeLater() method once the bitmap has been received. After the network operation completes, the UI must be updated. But that should not be done on the background thread. So, you create a Runnable to run on the background thread, and then once the download is complete, you create another Runnable to update the UI:
public void run() {
imagebitmap[j] = bmp;
invalidate();
}
2. Because I create another Runnable, and use the bmp variable inside that Runnable, I must declare it as a final parameter. The compiler requires you to do that. Another option would be to use the event lock directly, instead of invokeLater():
public void ImageDownloadCompleted(Bitmap bmp) {
synchronized(UiApplication.getEventLock()) {
imagebitmap[j] = bmp;
invalidate();
}
}
Either should work for you.
I want to change a progress bar use SetValue(int),but it doesn't work,it always change directly from 0 to 100 when progress finish.I try to create a new thread to invoke "setValue(int)",instead of invoking form UI thread,but it's still not worked.
my code:
public class UpdateProgressBar extends Thread{
public UpdateProgressBar(javax.swing.JProgressBar progressBar){
this.progressBar = progressBar;
}
public void update(){
for(int i = 1; i <= 100; i++){
progressBar.setValue(i);
try {
sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(UpdateProgressBar.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private javax.swing.JProgressBar progressBar;
}
,progressBar is defined in UI thread,then I UpdateProgressBar upb = new UpdateProgressBar(progressBar); in UI thread and invoke it's update wayupb.update();did I make some mistake?
Although you have declared UpdateProgressBar to extend Thread you are not actually running it as a separate thread. You need to call start() to make the new thread actually run. If you call upb.update() from the event dispatch thread then you are executing that method on the event dispatch thread.
You want this in your client code:
UpdateProgressBar upb = new UpdateProgressBar(progressBar);
upb.start();
and change your UpdateProgressBar class to this:
public class UpdateProgressBar extends Thread{
public UpdateProgressBar(javax.swing.JProgressBar progressBar){
this.progressBar = progressBar;
}
#Override
public void run(){
for(int i = 1; i <= 100; i++) {
final int j = i;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressBar.setValue(j);
}
});
try {
sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(UpdateProgressBar.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private javax.swing.JProgressBar progressBar;
}
You need to have the SwingUtilities.invokeLater because Swing components are not thread-safe. You also need the nasty final int j = i because of Java's less-than-brilliant handling of closures. Putting your code into the run method means it will be executed when you call Thread.start().
Perhaps the Maximum value set for it is 1? Set the progress bar's maximum before you start setting the value and see what happens.
below is my simple code to start 5 threads, each one calls a wcf service which returns the value sent in, my problem is that the :
public void clien_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
lock (sync)
{
count += e.Result;
}
}
works ok and increments the count, but how do i capture when all the threads have completed, does anybody have simple example code on how to call multiple wcf services which use async methods.
public partial class Threading : Form
{
public int count;
ServiceReference1.Service1Client clien = new ServiceReference1.Service1Client();
public Threading()
{
InitializeComponent();
}
private void GetData()
{
clien.GetDataAsync(1);
}
public void DisplayResults()
{
MessageBox.Show(count.ToString());
}
private object sync = new object();
public void clien_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
lock (sync)
{
count += e.Result;
}
}
public List<Thread> RunThreads(int count, ThreadStart start)
{
List<Thread> list = new List<Thread>();
for (int i = 0; i <= count - 1; i++)
{
dynamic thread = new Thread(start);
thread.Start();
list.Add(thread);
}
return list;
}
private void button1_Click_1(object sender, EventArgs e)
{
clien.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(clien_GetDataCompleted);
ThreadStart WcfCall = new ThreadStart(GetData);
IList<Thread> threads = RunThreads(5, WcfCall);
}
}
many thanks
If you are using .NET 4.0 you can use Task Parallel Library (TPL) and use Tasks instead of Threads. Tasks has more flow control. What you can do with tasks something like
// Wait for all the tasks to finish.
Task.WaitAll(tasks);
Here is example on how to use Tasks and wait for all tasks to finish. here
I have implemented the solution using tasks, the code is below, its works well, let me know if theres any improvement i could do.
public partial class Tasks : Form
{
static ServiceReference1.Service1Client clien = new ServiceReference1.Service1Client();
int count = 0;
public Tasks()
{
InitializeComponent();
}
// Define a delegate that prints and returns the system tick count
Func<object, int> action = (object obj) =>
{
int i = (int)obj;
clien.GetDataAsync(i);
Console.WriteLine("Task={0}, i={1}, Thread={2}", Task.CurrentId, i, Thread.CurrentThread.ManagedThreadId);
return i;
};
public void clien_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
count += e.Result;
}
private void button1_Click(object sender, EventArgs e)
{
const int n = 5;
// create async callback delegate from wcf.
clien.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(clien_GetDataCompleted);
// Construct started tasks
Task<int>[] tasks = new Task<int>[n];
for (int i = 0; i < n; i++)
{
tasks[i] = Task<int>.Factory.StartNew(action, i);
}
try
{
// Wait for all the tasks to finish.
Task.WaitAll(tasks);
MessageBox.Show(count.ToString());
}
catch
{
}
}
}
cheers