Execute the last thread in threadloop - c#

I am implementing a comic reader in C# 4.0, and browsing from one image
to the next takes some time because of some processes I implemented.
Therefore, I implemented it in such a way that the UI thread will show
first the unprocessed image whilst the background thread is processing
the image and will later replace the unprocessed image.
It all works fine but now, some users will crazily like to click next
image continusly and this causes the background worker to processes
all those clicks and display all the images.
WHAT I WANT: if a user clicks multiple times, I want the background
worker to process the last thread only.
WHAT I HAVE DONE: Now, I have implemented a function to check the
number of active threads, if the active threads is greater than 1,
background thread will not process but returns the previous image(THAT
IS NOT GOOD, because the unprocessed image will be one index ahead)
If you have idea please explain to me like a beginner!
private void button4_Click(object sender, EventArgs e)
{
Bitmap b = new Bitmap(this.CurrImage);
if (!shutdown)
{
process_updateThread = new Thread(new ThreadStart(process_update));
process_updateThread.Start();
}
pictureBox1.Image = b; //image will be replaced by worker thread image
pictureBox1.Location = ImageEdit.CalculateLocationImage(b);
SetBackColor(b);
ShowPageCount();
updateNavButtons();
}
void StopThread()
{
if(((IEnumerable)System.Diagnostics.Process.GetCurrentProcess().Threads).OfType<System.Diagnostics.ProcessThread>()
.Where(t => t.ThreadState == System.Diagnostics.ThreadState.Running).Count() > 1)
shutdown = true;
else shutdown = false;
}

I am assuming your long running process is process_update.
You must stop all running process_updates before running the next one. But DON'T USE BOOLEAN VARIABLE TO DO THAT!!! You must use synchronizing objects. Most likely it should be ManualResetEvent.
UPDATE:
This very simple example can give you an idea of multithreading and thread management
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;
namespace WindowsFormsExamples
{
public partial class OnlyOneThread : Form
{
List<ManualResetEvent> threadStopEvents; //This will hold stop events for running threads
public OnlyOneThread()
{
InitializeComponent();
threadStopEvents = new List<ManualResetEvent>();
}
private void runThreadBtn_Click(object sender, EventArgs e)
{
ManualResetEvent evt = new ManualResetEvent(false);
ParameterizedThreadStart ts = new ParameterizedThreadStart(this.ThreadFunc);
Thread t = new Thread(ts);
t.Start(evt);
}
private delegate void UptadeThreadCountDelegate(); //This delegate is used by Invoke method
private void UpdateThreadCount()
{
threadcountLbl.Text = threadStopEvents.Count.ToString();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
//We must stop threads if they are still running
lock (threadStopEvents) // locking prevents simultaneous list access
{
foreach (ManualResetEvent evt in threadStopEvents)
{
evt.Set(); //signal all events
}
}
}
//This is thread function
private void ThreadFunc(Object obj)
{
ManualResetEvent stopEvent = obj as ManualResetEvent; //cast an object that was passed by Thread.Start()
lock (threadStopEvents) // locking prevents simultaneous list access
{
foreach (ManualResetEvent evt in threadStopEvents)
{
evt.Set(); //signal all events for all other threads to stop
}
threadStopEvents.Add(stopEvent); //Put our event on list
}
if (this.IsHandleCreated) // This is necessary for invocation
this.Invoke(new UptadeThreadCountDelegate(this.UpdateThreadCount)); //Invoke counter update
for (int i = 0; i < 60; i++) // this will run about 1 minute
{
if (stopEvent.WaitOne(0)) // Tests stopEvent and continues
{
//Stop signaled!!! exit!
break;
}
Thread.Sleep(1000); //Sleep 1 second
}
lock (threadStopEvents) // locking prevents simultaneous list access
{
threadStopEvents.Remove(stopEvent); //remove stop event from list
}
if (this.IsHandleCreated) // This is necessary for invocation
this.Invoke(new UptadeThreadCountDelegate(this.UpdateThreadCount)); //Invoke counter update
}
}
}
If you want to run this example you must create WindowsForms project and add Button and label on the form, then use this code to bind to those controls. Note an invocation of forms method. This is necessary when don from non-GUI threads.

I can not see an easy solution to this problem... Multithreading is never easy. Personally, I would propose following (a sort of deviation of the producer/consumer situation):
First have a general counter which signifies the currently to be rendered image (can be a simple int which is incremented for each button pressed)
An ImageMonitor which is nicely locked and has a methods to:
add an image to be rendered (with the current counter) -> this happens for each button pressed
retrieve the image which should be rendered (including the counter of the image)
Process a rendered image
Now we need a continously working Background thread which loops and in every iteration checks the ImageMonitor for the newest image to process, processes the image and returns it back to the ImageMonitor (including the counter)
When the ImageMonitor gets a rendered image from the background renderer, then it can check if the image has the correct counter value, if so then it can swap the current image with the rendered image
This solution is obviously a little complicated. However, it should work. I'm interested in other (easier) solutions.
Good luck

Related

Status Listbox update problem using a Background Worker in C#

I'm trying to use a background worker to update a listbox used for a status window in my Form in C#. It doesn't appear to work properly when the addToStausLog() method is called from another class outside of the MyForm class even though I pass an instance of the form to the other class that's calling the addToStatusLog update member. Instead the update doesn't happen until the class member finished and returns back to the MyForm class. Maybe there's a better a approach to creating real-time status windows that will run from any class that MyForm is passed into. I'm new to worker threads, so could someone review and let me know what I might be doing wrong or could improve on.
public MyForm()
{
InitializeComponent();
// Setup background task to update listbox status so UI is unaffected
_lListBoxQue = new List<string>();
bw_listBoxBGWorker = new BackgroundWorker();
bw_listBoxBGWorker.DoWork += (o, args) => LstbxThread_doWork();
bw_listBoxBGWorker.RunWorkerCompleted += (o, args) => LstbxThread_completed();
}
private void LstbxThread_doWork()
{
System.Threading.Thread.Sleep(100);
}
private void LstbxThread_completed()
{
// Update listbox
lstStatusBox.BeginUpdate();
lstStatusBox.Items.Clear(); // clear entries
lstStatusBox.Items.AddRange(_lListBoxQue.ToArray());
lstStatusBox.EndUpdate();
}
public String addToStatusLog(String sMsg)
{
_lListBoxQue.Add(sMsg);
if (_lListBoxQue.Count > _iStatusLogMaxLines) // > max?
_lListBoxQue.RemoveAt(0); // remove top element?
if( !bw_listBoxBGWorker.IsBusy ) // background not busy?
bw_listBoxBGWorker.RunWorkerAsync(); // update listbox in back ground task
System.Threading.Thread.Sleep(100);
return sMsg;
}
This is the member that calls another class which attempts to call the addToStatusLog several times during the process, but the updates to the listbox don't happen until the MyClass(this).updateDB() finishes. I need to see real-time updates as the updateDB() function is running. There has to be a way to make this work, I'm hoping...
private void btnUpdateDB_Click(object sender, EventArgs e)
{
if (_bIsUpdateEventRunning == false ) // is event not busy?
{
_bIsUpdateEventRunning = true;
new MyClass(this).updateDB();
_bIsUpdateEventRunning = false;
}
}
Example of class called to update the form listbox.
Public class MyClass{
private MyForm _pForm;
public MyClass(MyForm pForm){ _pForm= pForm; }
public void updateDB(){
_pForm.addToStatusLog("Hello World");
}
}
Updated Fix w/o background worker:
public String addToStatusLog(String sMsg)
{
_lListBoxQue.Add(sMsg);
if (_lListBoxQue.Count > _iStatusLogMaxLines) // > max?
_lListBoxQue.RemoveAt(0); // remove top element?
lstStatusBox.BeginUpdate();
lstStatusBox.Items.Clear(); // clear entries
lstStatusBox.Items.AddRange(_lListBoxQue.ToArray());
lstStatusBox.EndUpdate();
Application.DoEvents();
return sMsg;
}
Thread.Sleep is not the answer here. What you likely need is Application.DoEvents. This processes all messages currently waiting in the Windows message queue.
Thread.Sleep just tells the thread to go to sleep for the number of milliseconds you specify. If your background worker is running on the UI thread, you're putting the UI thread to sleep and it's effectively comatose. (Important: All Windows forms run on the UI thread.)
There are, of course, alternative designs that involve spinning up separate threads of execution. But these have their own issues, and you should be mindful of them before running blindly down that path.

How to pass booleon back from Worker_ProgressChanged to Worker_DoWork

I'm using a Background worker to read values in and to pass values to Worker_ProgressChanged, to update UI.
In Worker_DoWork:
while (agi.DvmReadyToRead) // wait for digipot to be adjusted before reading in worker
{
Thread.Sleep(20);
Application.DoEvents();
//logS.Debug("Waiting for ready to read in worker");
}
Thread.Sleep(40); // Give digipot chance to make the change
agi.SendSoftwareTriggerOne();
Thread.Sleep(7); // Duration for above command to execute
A = agi.ReadOne();
Thread.Sleep(1);
agi.InitOne();
Thread.Sleep(1);
sAndH3 = A[0];
worker.ReportProgress(0, new System.Tuple<double>(sAndH3));
agi.DvmReadyToRead = true;
In Worker_ProgressChanged:
while (!agi.DvmReadyToRead)
{
//logS.Debug("waiting for ready to read in progress");
Thread.Sleep(0);
Thread.Sleep(0);
Thread.Sleep(0);
Thread.Sleep(0);
Thread.Sleep(0);
Application.DoEvents(); // Exception thown here
Thread.Sleep(1); // wait for DVM reading
}
agi.DvmReadyToRead = false;
// Then goes on to adjust output voltage up or down
This is working fine the first time round using
Application.DoEvents();
however after first run, I get a stackoverflow at this point. After reading many posts on here DoEvents is not the best way of doing what I am trying to achieve.
So what I would like is a way to pass a Boolean back to DoWork, or another way to allow worker to be able to read the agi.DvmReadyToRead Boolean.
Thanks!
If I understand your question, you are describing a very common pattern in Test and Measurement where you have an instrument that takes some time after triggering it before it gets a reading. But you want to know when the reading happens so that you can take some action (like update a ProgressBar or TextBox perhaps) and you want be able to cancel the worker loop.
When I need to do this myself, I like to use the System.Threading.Tasks to simplify things. I'll post a complete pattern here in the hope that you can find something of use to solve the issue you are having.
To be clear, I am trying to answer your question of "a way to pass a Boolean back to DoWork..." by saying that one way to do this is to fire an Event from Worker_DoWork that can contain Boolean (like you asked) or double (in my example) or any other information you choose.
Good luck!
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace StackOverflow02
{
public partial class DVMLoopRunner : Form
{
public DVMLoopRunner()
{
InitializeComponent();
DVMReadingAvailable += Form1_DVMReadingAvailable;
ContinueOrCancel += Form1_ContinueOrCancel;
}
// See if User has turned off the Run button then cancel worker
private void Form1_ContinueOrCancel(Object sender, CancelEventArgs e)
{
e.Cancel = !checkBoxRunMeterLoop.Checked;
}
// The DVM, after being triggered + some delay, has come up with a new reading.
private void Form1_DVMReadingAvailable(Object sender, DVMReadingAvailableEventArgs e)
{
// To update GUI from worker thread requires Invoke to prevent Cross-Thread Exception
Invoke((MethodInvoker)delegate
{
textBox1.Text = e.Reading.ToString("F4");
});
}
// Make our events so that we can be notified of things that occur
public event CancelEventHandler ContinueOrCancel;
public event DVMReadingAvailableEventHandler DVMReadingAvailable;
// This is how we will provide info to the GUI about the new reading
public delegate void DVMReadingAvailableEventHandler(Object sender, DVMReadingAvailableEventArgs e);
public class DVMReadingAvailableEventArgs : EventArgs
{
public readonly double Reading;
public DVMReadingAvailableEventArgs(double reading)
{
Reading = reading;
}
}
// When the User checks the box, Run the worker loop
private void checkBoxRunMeterLoop_CheckedChanged(Object sender, EventArgs e)
{
if(checkBoxRunMeterLoop.Checked)
{
Task.Run(() => ReadDVMWorker());
}
}
// Worker Loop
private void ReadDVMWorker()
{
while(true)
{
CancelEventArgs e = new CancelEventArgs();
ContinueOrCancel?.Invoke(this, e);
if (e.Cancel) return; // If User has turned off the Run button then stop worker
ReadDVM(); // This worker thread will block on this. So trigger, wait, etc.
}
}
// DVM Takes some period of time after trigger
void ReadDVM()
{
Thread.Sleep(1000);
double newSimulatedReading = 4.5 + Random.NextDouble();
DVMReadingAvailable?.Invoke(this, new DVMReadingAvailableEventArgs(newSimulatedReading));
}
Random Random = new Random(); // Generate random readings for simulation
}
}

c# threading with manual reset events

I have an application that imports data read from text files from a directory into a database. I have a UI that allows the user to click an import button and begin importing data and when the user clicks on that button again I wanted to stop importing the data in those files. I began using threads to allow this, so that I would not freeze up the UI while data was being imported. But Im having a few issues. I started using thread.Abort() to kill the thread after the user stops wanting to import but when the user clicks import again, some duplicate data is added to the database because it begins reading at the top of the text file which I dont want. I have been told to use ManualResetEvents and Thread.Join() to trigger for the import to finish, but im confused how that is supposed to work. Right now my code looks like this:
public ManualResetEvent event1 = new ManualResetEvent(false);
public Thread workerThread;
public Form1
{
InitializeComponent();
}
private void importButton_Click(object sender, EventArgs e)
{
if(importButton.Text == "Begin Import")
{
importButton.Text = "Stop Import";
//runs a service that begins reading and importing data and writing to
//a "console" box.
Service service = new Service(consoleBox);
//run the new thread to begin importing data
workerThread = new Thread(service.importData);
workerThread.Start();
}
else
{
importButton.Text = "Begin Import";
event1.Set();
while(!event1.WaitOne(TimeSpan.FromSeconds(4)))
{ //imports data for 30 more text files
service.importData(30);
workerThread.Join();
}
}
}
Basically what im trying to do is to keep the tread looping and checking to see if there is any files to be read, if there is then import The Data otherwise sleep for 4 seconds. Should I be using a threading Timer for this? I am a bit unsure of what to do.
Do not, in any way, block the UI thread by calling Thread.Join or ManualResetEvent.WaitOne. This will do exactly what you were trying to prevent; freeze the UI. Instead you need to create the MRE with its state initially set to true. Then in the importData method you need to periodically call WaitOne to see if importing should proceed (when the event is signaled) or pause (when the event is unsignaled).
Here is a rough sketch of how you would call WaitOne inside the importData method. Obviously, you would need to make adjustments to fit it into your specific implementation.
private void importData()
{
foreach (string filePath in GetFilesInSomeDirectory())
{
event1.WaitOne(); // Block when the MRE is unsignaled.
}
}
Then from your importButton.Click event handler you can call event1.Reset to pause the import operation or event1.Set to resume it.
Also, you should try to avoid calling Thread.Abort at all costs. It usually leads to more problems unless extra-special-nearly-impossible care is taken to avoid corrupting the state of the AppDomain.
Use timer for running the import process instead of thread, and define a variable to check if user request to stopinstead of thread.Abort() which by the way should be avoided.
In this code use System.Timers.Timer. and flag AutoReset property to false, so only import data if user not request to stop.
private System.Timers.Timer _importTimer = new System.Timers.Timer();
private volatile bool _requestStopImport = false;
public Form1()
{
InitializeComponent();
_importTimer.Interval = 4000;//4 seconds
_importTimer.AutoReset = false;//not automatically raise elapse event each time interval elapsed, only if we don't want to stop.
_importTimer.Elapsed += OnImportTimerElapced;
}
private void importButton_Click(object sender, EventArgs e)
{
if (importButton.Text == "Begin Import")
{
importButton.Text = "Stop Import";
StartImport();
}
else
{
importButton.Text = "Begin Import";
StopImport();
}
}
private void OnImportTimerElapced(object sender, System.Timers.TimerEventArgs e)
{
//runs a service that begins reading and importing data and writing to
//a "console" box.
Service service = new Service(consoleBox);//or maybe this would be a class level variable
service.importData();
if (!_requestStopImport)
{
_importTimer.Start();
}
}
private void StartImport()
{
_requestStopImport = false;
_importTimer.Start();
}
private void StopImport()
{
_requestStopImport = true;
_importTimer.Stop();
}
As you notice you don't have to use ManualResetEvent here. however if you want to be notified when code is completed or so you can use AutoResetEvent or raise an event for more detailed example check this.

GDI+ draw when loading data like a map

I have an application that finds the shortest path between 2 squares, and when the path is longer or more complicate it can take 1-2 seconds to find it and I want to write on the screen a loading message that changes (first "Loading" then "Loading." then "Loading.." etc.).
Another problem is that the application give a "Not Responding" message if it take longer (10-12 seconds) how can I get rid of this?
The code so far:
Form1.cs:
namespace PathFinder
{
Map map1;
public Form1()
{
map1 = new Map(tileDimension, mapDimension);
map1.Generate(); //the function that calculate the path
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
//drawings
this.Invalidate();
}
}
Map.cs:
namespace PathFinder
{
public Map(Point tileDim, Point mapDim)
{
//Initialization
}
public Generate()
{
//lots of loops
}
}
The reason for this is that UI main thread must process events.
If it does not for a period, it starts complaining, that is what you are experiencing.
So, you should not block the UI thread with any lengthy processing.
Use the BackgroundWorker Class for such operations.
Another option (not recommended) would be using
for...
{
// ...
// Some lengthy part of processing, but not as lengthy as the whole thing
Application.DoEvents();
}
in-between the lengthy operation cycles, if you choose to do processing in the UI thread.
Use a BackgroundWorker to offload long-running calculations on a worker thread. That prevents the UI from freezing. BGW is well covered by the MSDN Library, be sure to follow the examples.
Any drawing you have to do however still needs to be done on the UI thread with the Paint event. Just make sure that you can do so as quickly as possible. Have the worker store the path in, say, a Point[] or a GraphicsPath. Call Invalidate() in the BGW's RunWorkerCompleted event handler to get the paint event to run.
Seeing your code might help.To avoid window to halt you may use seperate thread for calculations or in your process you may use Applications.DoEvents(); if winform.
As i said.
Well Does this works?
namespace PathFinder
{
Map map1;
BackgroundWorker GetSomeData = new BackgroundWorker();
public Form1()
{
GetSomeData .DoWork += new DoWorkEventHandler(GetSomeData_DoWork);
map1 = new Map(tileDimension, mapDimension);
GetSomeData.RunWorkerAsync();
this.Invalidate();
}
void GetSomeData_DoWork(object sender, DoWorkEventArgs e)
{
map1.Generate(); //the function that calculate the path
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
//drawings
this.Invalidate();
}
}

Pause execution of a method without locking GUI. C#

I'm working on a card game in C# for a project on my Intro to OOP paper and have got the game working now but am adding "flair" to the GUI.
Currently cards are dealt and appear on the UI instantaneously. I want to have to program pause for a moment after dealing a card before it deals the next.
When a game is started the following code runs to populate the PictureBoxes that represent them (will be a loop eventually):
cardImage1.Image = playDeck.deal().show();
cardImage2.Image = playDeck.deal().show();
cardImage3.Image = playDeck.deal().show();
cardImage4.Image = playDeck.deal().show();
cardImage5.Image = playDeck.deal().show();
...
I have tries using System.Threading.Thread.Sleep(100); between each deal().show() and also inside each of those methods but all it achieves is locking up my GUI until all of the sleeps have processed then display all of the cards at once.
I have also tried using a combination of a timer and while loop but it resulted in the same effect.
What would be the best way of achieving the desired result?
The problem is that any code that you run on the UI will block the UI and freeze the program. When your code is running (even if it's running Thread.Sleep), messages (such as Paint or Click) sent to the UI will not be processed (until control returns to the message loop when you exit your event handler), causing it to freeze.
The best way to do this is to run on a background thread, and then Invoke to the UI thread between sleeps, like this:
//From the UI thread,
ThreadPool.QueueUserWorkItem(delegate {
//This code runs on a backround thread.
//It will not block the UI.
//However, you can't manipulate the UI from here.
//Instead, call Invoke.
Invoke(new Action(delegate { cardImage1.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage2.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage3.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
//etc...
});
//The UI thread will continue while the delegate runs in the background.
Alternatively, you could make a timer and show each image in the next timer tick. If you use a timer, all you should do at the beginning is start the timer; don't wait for it or you'll introduce the same problem.
Normally I'd simply recommend a function like this to perform a pause while allowing the UI to be interactive.
private void InteractivePause(TimeSpan length)
{
DateTime start = DateTime.Now;
TimeSpan restTime = new TimeSpan(200000); // 20 milliseconds
while(true)
{
System.Windows.Forms.Application.DoEvents();
TimeSpan remainingTime = start.Add(length).Subtract(DateTime.Now);
if (remainingTime > restTime)
{
System.Diagnostics.Debug.WriteLine(string.Format("1: {0}", remainingTime));
// Wait an insignificant amount of time so that the
// CPU usage doesn't hit the roof while we wait.
System.Threading.Thread.Sleep(restTime);
}
else
{
System.Diagnostics.Debug.WriteLine(string.Format("2: {0}", remainingTime));
if (remainingTime.Ticks > 0)
System.Threading.Thread.Sleep(remainingTime);
break;
}
}
}
But there seems to be some complication in using such a solution when it is called from within an event handler such as a button click. I think the system wants the button click event handler to return before it will continue processing other events because if I try to click again while the event handler is still running, the button depresses again even though I'm trying to drag the form and not click on the button.
So here's my alternative. Add a timer to the form and create a dealer class to handle dealing with cards by interacting with that timer. Set the Interval property of the timer to match the interval at which you want cards to be dealt. Here's my sample code.
public partial class Form1 : Form
{
CardDealer dealer;
public Form1()
{
InitializeComponent();
dealer = new CardDealer(timer1);
}
private void button1_Click(object sender, EventArgs e)
{
dealer.QueueCard(img1, cardImage1);
dealer.QueueCard(img2, cardImage2);
dealer.QueueCard(img3, cardImage1);
}
}
class CardDealer
{
// A queue of pairs in which the first value represents
// the slot where the card will go, and the second is
// a reference to the image that will appear there.
Queue<KeyValuePair<Label, Image>> cardsToDeal;
System.Windows.Forms.Timer dealTimer;
public CardDealer(System.Windows.Forms.Timer dealTimer)
{
cardsToDeal = new Queue<KeyValuePair<Label, Image>>();
dealTimer.Tick += new EventHandler(dealTimer_Tick);
this.dealTimer = dealTimer;
}
void dealTimer_Tick(object sender, EventArgs e)
{
KeyValuePair<Label, Image> cardInfo = cardInfo = cardsToDeal.Dequeue();
cardInfo.Key.Image = cardInfo.Value;
if (cardsToDeal.Count <= 0)
dealTimer.Enabled = false;
}
public void QueueCard(Label slot, Image card)
{
cardsToDeal.Enqueue(new KeyValuePair<Label, Image>(slot, card));
dealTimer.Enabled = true;
}
}
The cheap way out would be to loop with calls to Application.DoEvents() but a better alternative would be to set a System.Windows.Forms.Timer which you would stop after the first time it elapses. In either case you'll need some indicator to tell your UI event handlers to ignore input. You could even just use the timer.Enabled property for this purpose if it's simple enough.
I would try puting the code that deals the deck ( and calls Thread.Sleep) in another thread.

Categories

Resources