Using Auto reset event is not working as expected - c#

I am quite new to c# programming, I am trying to achieve the following result but failing to do so.
What I expect -:
On a click event of a button, I want to open an applciation via its API, run analysis and then exit the application. While running the application I have a progress bar on the form which should keep going from 0 - 100 till the RunAnalysis() method called through the API gets executed, when it gets executed the progress bar should show as 100% and the application called through should exit
What is happening -:
The RunanAlysis() is being executed and the application exits, the click event of the button gets executed and then the progress bar moves from 0 - 100 which should not happen
What is my attempt
namespace trialapp
{
public partial class Form1 : Form
{
AutoResetEvent obj = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
}
ETABS2015.cSapModel SapModel;
System.Reflection.Assembly ETABSAssembly;
ETABS2015.cOAPI ETABSObject;
int result = -1;
delegate int MyDelegate();
MyDelegate pointer = null;
private void button1_Click(object sender, EventArgs e)
{
//Use ret to check return values of OAPI calls
int ret;
//Dynamically load ETABS.exe assembly from the program installation folder
string pathToETABS = System.IO.Path.Combine(Environment.GetEnvironmentVariable("PROGRAMFILES"), "Computers and Structures", "ETABS 2013", "ETABS.exe");
ETABSAssembly = System.Reflection.Assembly.LoadFrom(pathToETABS);
//Create an instance of ETABSObject and get a reference to cOAPI interface
ETABSObject = (ETABS2015.cOAPI)ETABSAssembly.CreateInstance("CSI.ETABS.API.ETABSObject");
//Start ETABS application
ret = ETABSObject.ApplicationStart();
//Get a reference to cSapModel to access all OAPI classes and functions
SapModel = ETABSObject.SapModel;
//Initialize model
ret = SapModel.InitializeNewModel();
//Create steel deck template model
ret = SapModel.File.NewSteelDeck(4, 12, 12, 4, 4, 24, 24);
//Save model
System.IO.Directory.CreateDirectory("C:\\ETABSAPI");
ret = SapModel.File.Save("C:\\ETABSAPI\\example2.edb");
//Run analysis
backgroundWorker1.RunWorkerAsync();
// ret = SapModel.Analyze.RunAnalysis();
obj.WaitOne();
//Close ETABS
ret = ETABSObject.ApplicationExit(false);
//Clean up variables
SapModel = null;
ETABSObject = null;
//Check ret value
if (ret == 0)
{
MessageBox.Show("API script completed succesfully.");
}
else
{
MessageBox.Show("API script FAILED to complete.");
}
}
public void AfterRunAnalysisComplete(IAsyncResult resultHolder)
{
result = pointer.EndInvoke(resultHolder);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
pointer = new MyDelegate(SapModel.Analyze.RunAnalysis);
IAsyncResult flag = pointer.BeginInvoke(new AsyncCallback(AfterRunAnalysisComplete), null);
while (!flag.IsCompleted)
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
backgroundWorker1.ReportProgress(i);
if (i == 100)
{
i = 0;
}
if (flag.IsCompleted)
{
break;
}
}
}
backgroundWorker1.ReportProgress(100);
//obj.Set();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
}
}
Can any one tell me as to where exactly am I going wrong?
Edit -:
I tried not using WaitOne() and putting the code which followsbackgroundWorker1.RunWorkerAsync(); in the backgroundWorker1_DoWork method, but that is not I want to do as the extent of main project is too much and this will not make sense with the design of classes.

The problem, as I see it, is when you kick off
ret = ETABSObject.ApplicationStart();
it's going to start it in a new thread, which your program isn't going to have access to. I would recommend starting the application in a TaskFactory, then you can use a while to check if the task is still running and update your progress bar.
Maybe something like this:
var etabApp = Task.Factory.Startnew(() => { ETABSObject.ApplicationStart()});
while(etabApp.Status == TaskStatus.Running)
{
//Do something to check the percent complete and update the progress bar
}

Related

C# - WPF - Updating the UI from another class on another Thread

I have looked around the internet and found a nice solution which I am incorporating into my code below however it doesn't quite do exactly what I want, it works when just calling an update but I want to run a method in another class then let that method call the method that will report back to the UI and just pass some information so this mock up is just changing the button content before the operation is ran.
Using a Dispatcher I can get a UI control to update however I don't just wish to do that I want to perform some functions then have the UI Update.
So there maybe some theory I am not getting, I know the Invoke is a synchronous operation and breaking through the code it does run but the UI doesn't update.
MainWindow
Has a single button with content "CLICK ME"
Code Behind
public partial class MainWindow : Window
{
public static Button windowButton;
public MainWindow()
{
InitializeComponent();
windowButton = btnStart;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
// Run a Process in another class on a different Thread
ProcessClass processClass = new ProcessClass();
Task processTask = new Task(() =>
{
processClass.DoSomething();
});
processTask.Start();
}
}
}
ProcessClass
class ProcessClass:MainWindow
{
public static void UpdateUI()
{
App.Current.Dispatcher.Invoke(delegate
{
windowButton.Content = "CHANGED CONTENT";
});
}
public void DoSomething()
{
UpdateUI();
int counter = 0;
for(int i = 1; i < 100; i++)
{
counter += i;
Thread.Sleep(100);
}
MessageBox.Show($"Task Completed, answer is {counter}");
}
}
Assuming that ProcessClass is your own code that you can update, change the signiture of DoDomething() to
public async Task DoSomething(IProgress<string> progress)
{
progress.Report("Begin DoSomething()");
var counter = 0;
for(var i = 1; i < 100; i++)
{
counter += i;
await Task.Delay(100).ConfigureAwait(false);
progress.Report($"DoSomething() - i = {i}");
}
progress.Report($"DoSomething() Completed, answer is {counter}");
}
Now your button click handler can be written
private async void btnStart_Click(object sender, RoutedEventArgs e)
{
// usually you would update some other control such as a TextBlock
// for the feedback, rather than the button content
var progress = new Progress<string>(s => btnStart.Content = s);
ProcessClass processClass = new ProcessClass();
await processClass.DoSomething(progress).ConfigureAwait(false);
}

C#.net, raising events while processing a long task, show a form which can have "Pause" and "Continue" to continue processing

I process a file line by line reading various events that have a time stamp and data associated with them. I want to be able to show a form while doing processing which I need to interact with and intercept some events by having a button saying interrupt EventX and if it is pressed it will show the event data in a rich text box field when this event is reached sometime in the future. I can then change some of that event data (let's say I simulate some conditions) and when I press "Resume" it should resume processing by raising an event to the intended subscriber for further processing.
So I need an interceptor that will be pass-trough mechanism when a certain form element is pressed and pass that data to the intended subscriber.
I am ok to wait synchronously for modifying data and pressing "Resume"
Thanks
If you want to have a Responsive GUI while doing a long running operation, you need some form of Multitasking. Wich means either async/await or any of the many Multithreading (Thread and BackgroundWorker, mostly) approaches
While pause and resume could be added, doing so usually more work then it is worth. At the very least you run into issues like still held filehandles or race conditions. Often a "cancel" action is way enough/better then a full stop/resume mechanic.
As a Beginner I would advice you to use the BackgroundWorker. It is about as easy as getting into Multitasking via Multithreading can be. I even wrote a example for it a few years back:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
Thank you Christofer,
I accepted you answer as you gave me some suggestions how to solve my problem.
You can see bellow how I solved this problem
Rad
//class variable
private SimulatorRunner simulatorRunner;
//Code behind DevicesSimulatorForm form
private void RunSimulator()
{
btnRerun.BackColor = Color.BurlyWood;
ParameterizedThreadStart start = new ParameterizedThreadStart(RunSimulator);
Thread simulatorProcessingThread = new Thread(start);
simulatorProcessingThread.Start(this);
}
//This will run in a separate thread so when accessing controls Invoke is being used.
public void RunSimulator(object form)
{
DevicesSimulatorForm devicesSimulatorForm = (DevicesSimulatorForm) form;
simulatorRunner.Run(devicesSimulatorForm);
devicesSimulatorForm.InvokeEx(formInner =>
{
formInner.btnRerun.BackColor = Color.LightGray;
InitializeFields();
InitializeTextBackBorder();
InitializeButtonControls();
running = false;
});
}
public class SimulatorRunner
{
public void Run(DevicesSimulator form)
{
string buffer = "Some content read from file in a loop that needs to be passed
to a rich text box when a boolean Intercept check box is true
and FormStatusIntercept will return true and with Thread.Sleep(1)
we will have a chance to update the buffer to the new value and by
unchecking Intercept check box we will exit while loop and continue
processing"
while (true)
{
if (FormStatusIntercept(form, ref buffer))
{
Thread.Sleep(1);
}
else
{
publishEventArgs.Buffer = buffer;
break;
}
}
PublishEvent?.Invoke(this, publishEventArgs);
}
}
private bool FormStatusIntercept(DevicesSimulator simulatorForm, ref string buffer)
{
string modifiedBuffer = buffer;
//When btnFormStatus button is pressed it changes FormStatusContinued = true
//which allows continuation of the processing by exiting while loop
if (simulatorForm.FormStatusContinued == true)
{
simulatorForm.InvokeEx(form =>
{
if (form.rtbFormStatus.Text != modifiedBuffer)
{
modifiedBuffer = form.rtbFormStatus.Text;
}
form.FormStatusContinued = false;
form.FormStatusInterceptPending = false;
});
buffer = modifiedBuffer;
return false;
}
else if (simulatorForm.FormStatusIntercept == true)
{
if (simulatorForm.FormStatusInterceptPending == false)
{
//Whith check box pressed (true) we request a stop
//and we enter a while loop with Thread.Sleep(1)
simulatorForm.InvokeEx(form =>
{
form.btnFormStatus.Text = "Continue";
form.rtbFormStatus.Text = modifiedBuffer;
form.FormStatusInterceptPending = true;
});
}
return true;
}
return false;
}

Updating GUI from thread C#, without binding to UI controls

It is know that Invoke method is used when u need to update gui from other thread. But How can I implement this without binding control to code?
Here's my test class:
class test
{
public List<Thread> threads = new List<Thread>();
public int nThreads = 0;
public int maxThreads = 5;
public void DoWork(object data)
{
string message = (string)data;
//MessageBox.Show(message);
}
public void CreateThread(object data)
{
if (nThreads >= maxThreads)
return;
Thread newThread = new Thread(DoWork);
threads.Add(newThread);
newThread.IsBackground = true;
newThread.Start(data);
nThreads++;
}
public void WindUpThreads()
{
//MessageBox.Show("count: " + nThreads.ToString());
for(int i = 0; i < threads.Count; i++)
{
if (threads[i].IsAlive == false)
{
threads[i].Abort();
threads.RemoveAt(i);
//MessageBox.Show("removing at " + i.ToString());
}
}
nThreads = threads.Count;
}
}
The question is = what tecnique I must use in order to update gui but not hardcode control into class? I've tried to pass delegate to DoWork Method, but this doesn't work (http://pastebin.com/VaSYFxPw). Thanks!
I'm using WinForms, .NET 3.5
Here's the button_click handler:
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
test thTest = new test();
string[] strings;
try
{
strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
bool flag = true;
int counter = 0;
int dataCount = strings.Length;
while (flag == true)
{
if (counter >= dataCount)
{
flag = false;
}
while (thTest.nThreads < thTest.maxThreads)
{
if (flag == false)
break;
thTest.CreateThread(strings[counter]);
//Data d = new Data();
//d.deleg = AddItem;
//d.mess = strings[counter];
//thTest.CreateThread((object)d);
//MessageBox.Show(counter.ToString());
counter++;
}
thTest.WindUpThreads();
if (flag == false)
{
do
{
thTest.WindUpThreads();
} while (thTest.nThreads != 0);
}
}
listBox1.Items.Add("Done");
}
The idea is that I'am launching threads for each task I want to process. After while I'am checking are there completed tasks, then they being shutdowned and new ones are launched until there no more tasks left.
Rather than making DoWork responsible for updating the UI with the results of the operation it performs, simply have it return the value:
//TODO change the type of the result as appropriate
public string DoWork(string message)
{
string output = "output";
//TODO do some work to come up with the result;
return output;
}
Then use Task.Run to create a Task that represents that work being done in a thread pool thread. You can then await that task from your button click handler.
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
test thTest = new test();
//I'd note that you really should pull out reading in this file from your UI code;
//it should be in a separate method, and it should also be reading
//the file asynchronously.
string[] strings;
try
{
strings = System.IO.File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
foreach (var line in strings)
{
var result = await thTest.DoWork(line);
listBox1.Items.Add(result);
}
listBox1.Items.Add("Done");
}
If you really want to be old school about it, you can use a BackgroundWorker instead. Simply do your work in the DoWork handler, setting the result (through the argument) when you've computed it, and update the UI with the result in the RunWorkerCompleted event handler. This lets you keep the UI and non-UI work separate, although it's far less powerful, general purpose, and extensible, as the newer features.
The question is = what tecnique I must use in order to update gui but not hardcode control into class? I've tried to pass delegate to DoWork Method, but this doesn't work
This is indeed the one of the possible techniques. It doesn't work because you have a blocking loop in the UI thread - the most of the code inside the button1_Click handler. It doesn't matter that you spawn additional worker threads - that code keeps the UI thread busy, thus Control.Invoke / Control.BeginInvoke doesn't work because they are processed by the UI thread message loop, which in this case has no chance to do that. The end result is a classical deadlock.
So, you can use the delegate approach, but to make it work, you need to move that code in a separate thread. Something like this
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
var worker = new Thread(DoWork);
worker.IsBackground = true;
worker.Start();
}
private void OnWorkComplete(Exception error)
{
if (error != null)
MessageBox.Show(error.Message);
button1.Enabled = true;
}
private void DoWork()
{
Exception error = null;
try { DoWorkCore(); }
catch (Exception ex) { error = ex; }
Invoke(new Action(OnWorkComplete), error);
}
private void DoWorkCore()
{
test thTest = new test();
// NOTE: No try/catch for showing message boxes, this is running on a non UI thread
string[] strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt");
bool flag = true;
int counter = 0;
int dataCount = strings.Length;
// The rest of the code...
// Pass a delegate to the other threads.
// Make sure using Invoke when you need to access/update UI elements
}

Move code that run on a UI thread and access global states to a thread

I am trying to move as much processing out of the UI thread on my Windows Phone app. I have some code that is being executed when I click on a button. The code is conceptually similar to the code below.
private int Processing(int a, int b, int c) {
this.A = this.moreProcessing(a);
this.B = this.moreProcessing(b);
this.C = this.moreProcessing(c);
int newInt = /* ... */
return newInt;
}
public void Button_Click(object sender, EventArgs args) {
var result = Processing(1, 2, 3);
this.MyTextBox.Content = result;
}
That would be very easy to move the execution on that code on a thread if the Processing method wasn't setting/getting global state variables.
How do I make sure that only one thread at a time is running in the right sequence? Right now it is easy since the processing code runs on the UI thread. The nice thing about the UI thread is that it guarantee me that everything runs in the right order and one at a time. How do I replicate that with threads?
I could refactor the entire code to have almost no global state, but cannot necessarily do that right now. I could also use lock, but I am just wondering if there's a better way. The processing I am doing isn't super heavy. However, I sometime see some lag in the UI and I want to keep the UI thread as free as possible.
Thanks!
There are a few approaches.
If you intend to fire up a new thread for every Button_Click event, then indeed you could have multiple threads that wish to write to the same variables. You can solve that by wrapping the access to those variables in a lock statement.
Alternatively, you could have one thread always running dedicated to the Processing thread. Use a BlockingCollection to communicate between the UI thread and the Processing thread. Whenever a Button_Click happens, place the relevant info on the BlockingCollection, and have the Processing thread pull work items off of that BlockingCollection.
Untested code that should be close to OK:
class ProcessingParams // Or use a Tuple<int, int, int>
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
BlockingCollection<int> bc = new BlockingCollection<int>();
private int Processing() {
try
{
while (true)
{
ProcesingParams params = bc.Take();
this.A = this.moreProcessing(params.A);
this.B = this.moreProcessing(params.B);
this.C = this.moreProcessing(params.C);
int newInt = /* ... */
return newInt; // Rather than 'return' the int, place it in this.MyTextBox.Content using thread marshalling
}
}
catch (InvalidOperationException)
{
// IOE means that Take() was called on a completed collection
}
}
public void Button_Click(object sender, EventArgs args) {
//var result = Processing(1, 2, 3);
bc.Add (new ProcessingParams() { A = 1, B = 2, C = 3 };
//this.MyTextBox.Content = result;
}
When your application closes down, remember to call
bc.CompleteAdding(); // Causes the processing thread to end
A very simple solution is to use a BackgroundWorker. It allows you to offload your work to a background thread and notify you when it is complete. (see below for another option)
void Button_Click(object sender, EventArgs args)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
e.Result = Processing(1, 2, 3);
};
worker.RunWorkerCompleted += (s1, e1) =>
{
MyTextBox.Content = e1.Result;
MyButton.IsEnabled = true;
};
// Disable the button to stop multiple clicks
MyButton.IsEnabled = false;
worker.RunWorkerAsync();
}
Another option is to get your code ready for the next version of Windows Phone and start using the Task Parallel Library. TPL is available with .Net4, but is not available with Windows Phone. There are some NuGet packages that do support Silverlight and Windows Phone. Add one of these packages to your project and you can change your code to (syntax may not be 100% correct):
private Task<int> ProcessAsync(int a, int b, int c)
{
var taskCompletionSource = new TaskCompletionSource<int>();
var task = Task.Factory.StartNew<int>(() =>
{
// Do your work
return newInt;
}
task.ContinueWith(t => taskCompletionSource.SetResult(t.Result));
return taskCompletionSource.Task;
}
void Button_Click(object sender, EventArgs args)
{
// Disable the button to prevent more clicks
MyButton.IsEnabled = false;
var task = ProcessAsync(1,2,3);
task.ContinueWith(t =>
{
MyTextBox.Content = t.Result;
MyButton.IsEnabled = true;
});
}
Try this:
public void Button_Click(object sender, EventArgs args)
{
Button.Enabled = false;
ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundProcessing));
}
private void BackgroundProcessing(object state)
{
var result = Processing(1, 2, 3);
// Call back to UI thread with results
Invoke(new Action(() => {
this.MyTextBox.Content = result;
Button.Enabled = true;
}));
}
private int Processing(int a, int b, int c)
{
this.A = this.moreProcessing(a);
this.B = this.moreProcessing(b);
this.C = this.moreProcessing(c);
int newInt = /* ... */
return newInt;
}

Timer control not working in ASP.net

I have a situation like this.
TestClass - a class defined in UI Layer
test - a class level variable
Submit button calls a function DoSomeThing()..It performs some operation in Busines Layer keeps on updating the Status Property of the class
Timercontrol getting the status (from same variable test)to display in UI
Submit button runs by a thread (say thread 1 ) and starts the operation. Is is this thread which updates the status property from the BL
Timer control creates a new thread each time to run the TimerEvent (Say thread 2 , 3 etc).
Issue here is that test.Status property , which is updated by thread1 is not accessible by other thread.. It is always null , even though the property has been updated by thread 1..
What is the solution for this ?
Thanks in advance
public class TestClass //---->#1
{
private test = new Test() ; //---->#2
protected void SubmitButon_Click(object sender, EventArgs e)
{
// This is performed by Thread1
test.DoSomeThing() //------>#3
}
protected void UpdateTimer_Tick(object sender, EventArgs e)
{
// Timer controls sends out a new thread each time
Label1.Text = test.Status; //------>#4
}
}
here's sample to use delegate and update UI ements from different thread
delegate string CallFunctionDelegate(string arg1, string arg2);
private void btnStart_Click(object sender, EventArgs e)
{
CallFunctionDelegate delegRunApps = new CallFunctionDelegate(DoSomeThingBig);
AsyncCallback CallBackAfterAsynOperation = new AsyncCallback(AfterDoingSomethingBig);
delegRunApps.BeginInvoke("", "", CallBackAfterAsynOperation, null);
}
private string DoSomeThingBig(string arg1, string arg2)
{
#region Implemetation of time consuming function
//Implemetation of time consuming function
for (int i = 0; i < 5; i++)
{
Thread.Sleep(1000);
if (btnStart.InvokeRequired)
{
btnStart.Invoke((new MethodInvoker(delegate { btnStart.Text = i.ToString(); })));
}
else
{
btnStart.Text = i.ToString();
}
}
#endregion
return arg1.Replace("freetime", arg2);
}
private void AfterDoingSomethingBig(IAsyncResult result)
{
MessageBox.Show("Finaly Done!! ;) ");
btnStart.Invoke((new MethodInvoker(delegate { btnStart.Text = "Start"; })));
}
Issue happens because a new instance is created by the timerthread eachtime after as Line #2 is executed..Hence test.Status is always null.. That was the reason for the issue

Categories